Merge pull request #76165 from and-rad/safe-credentials

Store sensitive export options in dedicated credentials file
This commit is contained in:
Rémi Verschelde 2023-05-10 12:47:07 +02:00
commit 74e5ad5c60
No known key found for this signature in database
GPG Key ID: C3336907360768E1
22 changed files with 211 additions and 105 deletions

View File

@ -705,6 +705,7 @@ void register_global_constants() {
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_EDITOR_BASIC_SETTING);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_READ_ONLY);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_SECRET);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_DEFAULT);
BIND_CORE_BITFIELD_FLAG(PROPERTY_USAGE_NO_EDITOR);

View File

@ -119,6 +119,7 @@ enum PropertyUsageFlags {
PROPERTY_USAGE_EDITOR_INSTANTIATE_OBJECT = 1 << 26, // For Object properties, instantiate them when creating in editor.
PROPERTY_USAGE_EDITOR_BASIC_SETTING = 1 << 27, //for project or editor settings, show when basic settings are selected.
PROPERTY_USAGE_READ_ONLY = 1 << 28, // Mark a property as read-only in the inspector.
PROPERTY_USAGE_SECRET = 1 << 29, // Export preset credentials that should be stored separately from the rest of the export config.
PROPERTY_USAGE_DEFAULT = PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR,
PROPERTY_USAGE_NO_EDITOR = PROPERTY_USAGE_STORAGE,

View File

@ -2845,6 +2845,9 @@
<constant name="PROPERTY_USAGE_READ_ONLY" value="268435456" enum="PropertyUsageFlags" is_bitfield="true">
The property is read-only in the [EditorInspector].
</constant>
<constant name="PROPERTY_USAGE_SECRET" value="536870912" enum="PropertyUsageFlags" is_bitfield="true">
An export preset property with this flag contains confidential information and is stored separately from the rest of the export preset configuration.
</constant>
<constant name="PROPERTY_USAGE_DEFAULT" value="6" enum="PropertyUsageFlags" is_bitfield="true">
Default usage (storage, editor and network).
</constant>

View File

@ -37,7 +37,9 @@ EditorExport *EditorExport::singleton = nullptr;
void EditorExport::_save() {
Ref<ConfigFile> config;
Ref<ConfigFile> credentials;
config.instantiate();
credentials.instantiate();
for (int i = 0; i < export_presets.size(); i++) {
Ref<EditorExportPreset> preset = export_presets[i];
String section = "preset." + itos(i);
@ -83,16 +85,21 @@ void EditorExport::_save() {
config->set_value(section, "encryption_exclude_filters", preset->get_enc_ex_filter());
config->set_value(section, "encrypt_pck", preset->get_enc_pck());
config->set_value(section, "encrypt_directory", preset->get_enc_directory());
config->set_value(section, "script_encryption_key", preset->get_script_encryption_key());
credentials->set_value(section, "script_encryption_key", preset->get_script_encryption_key());
String option_section = "preset." + itos(i) + ".options";
for (const PropertyInfo &E : preset->get_properties()) {
config->set_value(option_section, E.name, preset->get(E.name));
if (E.usage & PROPERTY_USAGE_SECRET) {
credentials->set_value(option_section, E.name, preset->get(E.name));
} else {
config->set_value(option_section, E.name, preset->get(E.name));
}
}
}
config->save("res://export_presets.cfg");
credentials->save("res://.godot/export_credentials.cfg");
}
void EditorExport::save_presets() {
@ -202,6 +209,13 @@ void EditorExport::load_config() {
return;
}
Ref<ConfigFile> credentials;
credentials.instantiate();
err = credentials->load("res://.godot/export_credentials.cfg");
if (!(err == OK || err == ERR_FILE_NOT_FOUND)) {
return;
}
block_save = true;
int index = 0;
@ -284,22 +298,30 @@ void EditorExport::load_config() {
if (config->has_section_key(section, "encryption_exclude_filters")) {
preset->set_enc_ex_filter(config->get_value(section, "encryption_exclude_filters"));
}
if (config->has_section_key(section, "script_encryption_key")) {
preset->set_script_encryption_key(config->get_value(section, "script_encryption_key"));
if (credentials->has_section_key(section, "script_encryption_key")) {
preset->set_script_encryption_key(credentials->get_value(section, "script_encryption_key"));
}
String option_section = "preset." + itos(index) + ".options";
List<String> options;
config->get_section_keys(option_section, &options);
for (const String &E : options) {
Variant value = config->get_value(option_section, E);
preset->set(E, value);
}
if (credentials->has_section(option_section)) {
options.clear();
credentials->get_section_keys(option_section, &options);
for (const String &E : options) {
Variant value = credentials->get_value(option_section, E);
preset->set(E, value);
}
}
add_export_preset(preset);
index++;
}

View File

@ -818,6 +818,14 @@ String EditorExportPlatform::_export_customize(const String &p_path, LocalVector
return save_path.is_empty() ? p_path : save_path;
}
String EditorExportPlatform::_get_script_encryption_key(const Ref<EditorExportPreset> &p_preset) const {
const String from_env = OS::get_singleton()->get_environment(ENV_SCRIPT_ENCRYPTION_KEY);
if (!from_env.is_empty()) {
return from_env.to_lower();
}
return p_preset->get_script_encryption_key().to_lower();
}
Vector<String> EditorExportPlatform::get_forced_export_files() {
Vector<String> files;
@ -946,7 +954,7 @@ Error EditorExportPlatform::export_project_files(const Ref<EditorExportPreset> &
}
// Get encryption key.
String script_key = p_preset->get_script_encryption_key().to_lower();
String script_key = _get_script_encryption_key(p_preset);
key.resize(32);
if (script_key.length() == 64) {
for (int i = 0; i < 32; i++) {
@ -1577,7 +1585,7 @@ Error EditorExportPlatform::save_pack(const Ref<EditorExportPreset> &p_preset, b
Ref<FileAccess> fhead = f;
if (enc_pck && enc_directory) {
String script_key = p_preset->get_script_encryption_key().to_lower();
String script_key = _get_script_encryption_key(p_preset);
Vector<uint8_t> key;
key.resize(32);
if (script_key.length() == 64) {

View File

@ -43,6 +43,8 @@ struct EditorProgress;
class EditorExportPlugin;
const String ENV_SCRIPT_ENCRYPTION_KEY = "GODOT_SCRIPT_ENCRYPTION_KEY";
class EditorExportPlatform : public RefCounted {
GDCLASS(EditorExportPlatform, RefCounted);
@ -116,6 +118,7 @@ private:
bool _is_editable_ancestor(Node *p_root, Node *p_node);
String _export_customize(const String &p_path, LocalVector<Ref<EditorExportPlugin>> &customize_resources_plugins, LocalVector<Ref<EditorExportPlugin>> &customize_scenes_plugins, HashMap<String, FileExportCache> &export_cache, const String &export_base_path, bool p_force_save);
String _get_script_encryption_key(const Ref<EditorExportPreset> &p_preset) const;
protected:
struct ExportNotifier {

View File

@ -302,4 +302,15 @@ String EditorExportPreset::get_script_encryption_key() const {
return script_key;
}
Variant EditorExportPreset::get_or_env(const StringName &p_name, const String &p_env_var, bool *r_valid) const {
const String from_env = OS::get_singleton()->get_environment(p_env_var);
if (!from_env.is_empty()) {
if (r_valid) {
*r_valid = true;
}
return from_env;
}
return get(p_name, r_valid);
}
EditorExportPreset::EditorExportPreset() {}

View File

@ -152,6 +152,8 @@ public:
void set_script_encryption_key(const String &p_key);
String get_script_encryption_key() const;
Variant get_or_env(const StringName &p_name, const String &p_env_var, bool *r_valid = nullptr) const;
const List<PropertyInfo> &get_properties() const { return properties; }
EditorExportPreset();

View File

@ -58,21 +58,27 @@
</member>
<member name="keystore/debug" type="String" setter="" getter="">
Path of the debug keystore file.
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_DEBUG_PATH[/code].
</member>
<member name="keystore/debug_password" type="String" setter="" getter="">
Password for the debug keystore file.
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_DEBUG_PASSWORD[/code].
</member>
<member name="keystore/debug_user" type="String" setter="" getter="">
User name for the debug keystore file.
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_DEBUG_USER[/code].
</member>
<member name="keystore/release" type="String" setter="" getter="">
Path of the release keystore file.
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_PATH[/code].
</member>
<member name="keystore/release_password" type="String" setter="" getter="">
Password for the release keystore file.
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD[/code].
</member>
<member name="keystore/release_user" type="String" setter="" getter="">
User name for the release keystore file.
Can be overridden with the environment variable [code]GODOT_ANDROID_KEYSTORE_RELEASE_USER[/code].
</member>
<member name="launcher_icons/adaptive_background_432x432" type="String" setter="" getter="">
Background layer of the application adaptive icon file.

View File

@ -1825,12 +1825,12 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, vformat("%s/%s", PNAME("architectures"), abi)), is_default));
}
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0"));
@ -2277,9 +2277,9 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
// Validate the rest of the export configuration.
String dk = p_preset->get("keystore/debug");
String dk_user = p_preset->get("keystore/debug_user");
String dk_password = p_preset->get("keystore/debug_password");
String dk = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
String dk_user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
String dk_password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
if ((dk.is_empty() || dk_user.is_empty() || dk_password.is_empty()) && (!dk.is_empty() || !dk_user.is_empty() || !dk_password.is_empty())) {
valid = false;
@ -2294,9 +2294,9 @@ bool EditorExportPlatformAndroid::has_valid_export_configuration(const Ref<Edito
}
}
String rk = p_preset->get("keystore/release");
String rk_user = p_preset->get("keystore/release_user");
String rk_password = p_preset->get("keystore/release_password");
String rk = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
String rk_user = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
String rk_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
if ((rk.is_empty() || rk_user.is_empty() || rk_password.is_empty()) && (!rk.is_empty() || !rk_user.is_empty() || !rk_password.is_empty())) {
valid = false;
@ -2507,9 +2507,9 @@ void EditorExportPlatformAndroid::get_command_line_flags(const Ref<EditorExportP
Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_preset, bool p_debug, const String &export_path, EditorProgress &ep) {
int export_format = int(p_preset->get("gradle_build/export_format"));
String export_label = export_format == EXPORT_FORMAT_AAB ? "AAB" : "APK";
String release_keystore = p_preset->get("keystore/release");
String release_username = p_preset->get("keystore/release_user");
String release_password = p_preset->get("keystore/release_password");
String release_keystore = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
String release_username = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
String release_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
String target_sdk_version = p_preset->get("gradle_build/target_sdk");
if (!target_sdk_version.is_valid_int()) {
target_sdk_version = itos(DEFAULT_TARGET_SDK_VERSION);
@ -2529,9 +2529,9 @@ Error EditorExportPlatformAndroid::sign_apk(const Ref<EditorExportPreset> &p_pre
String password;
String user;
if (p_debug) {
keystore = p_preset->get("keystore/debug");
password = p_preset->get("keystore/debug_password");
user = p_preset->get("keystore/debug_user");
keystore = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
if (keystore.is_empty()) {
keystore = EDITOR_GET("export/android/debug_keystore");
@ -2886,9 +2886,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
if (should_sign) {
if (p_debug) {
String debug_keystore = p_preset->get("keystore/debug");
String debug_password = p_preset->get("keystore/debug_password");
String debug_user = p_preset->get("keystore/debug_user");
String debug_keystore = p_preset->get_or_env("keystore/debug", ENV_ANDROID_KEYSTORE_DEBUG_PATH);
String debug_password = p_preset->get_or_env("keystore/debug_password", ENV_ANDROID_KEYSTORE_DEBUG_PASS);
String debug_user = p_preset->get_or_env("keystore/debug_user", ENV_ANDROID_KEYSTORE_DEBUG_USER);
if (debug_keystore.is_empty()) {
debug_keystore = EDITOR_GET("export/android/debug_keystore");
@ -2908,9 +2908,9 @@ Error EditorExportPlatformAndroid::export_project_helper(const Ref<EditorExportP
cmdline.push_back("-Pdebug_keystore_password=" + debug_password); // argument to specify the debug keystore password.
} else {
// Pass the release keystore info as well
String release_keystore = p_preset->get("keystore/release");
String release_username = p_preset->get("keystore/release_user");
String release_password = p_preset->get("keystore/release_password");
String release_keystore = p_preset->get_or_env("keystore/release", ENV_ANDROID_KEYSTORE_RELEASE_PATH);
String release_username = p_preset->get_or_env("keystore/release_user", ENV_ANDROID_KEYSTORE_RELEASE_USER);
String release_password = p_preset->get_or_env("keystore/release_password", ENV_ANDROID_KEYSTORE_RELEASE_PASS);
if (release_keystore.is_relative_path()) {
release_keystore = OS::get_singleton()->get_resource_dir().path_join(release_keystore).simplify_path();
}

View File

@ -49,6 +49,15 @@ const String SPLASH_CONFIG_XML_CONTENT = R"SPLASH(<?xml version="1.0" encoding="
</layer-list>
)SPLASH";
// Optional environment variables for defining confidential information. If any
// of these is set, they will override the values set in the credentials file.
const String ENV_ANDROID_KEYSTORE_DEBUG_PATH = "GODOT_ANDROID_KEYSTORE_DEBUG_PATH";
const String ENV_ANDROID_KEYSTORE_DEBUG_USER = "GODOT_ANDROID_KEYSTORE_DEBUG_USER";
const String ENV_ANDROID_KEYSTORE_DEBUG_PASS = "GODOT_ANDROID_KEYSTORE_DEBUG_PASSWORD";
const String ENV_ANDROID_KEYSTORE_RELEASE_PATH = "GODOT_ANDROID_KEYSTORE_RELEASE_PATH";
const String ENV_ANDROID_KEYSTORE_RELEASE_USER = "GODOT_ANDROID_KEYSTORE_RELEASE_USER";
const String ENV_ANDROID_KEYSTORE_RELEASE_PASS = "GODOT_ANDROID_KEYSTORE_RELEASE_PASSWORD";
struct LauncherIcon {
const char *export_path;
int dimensions = 0;

View File

@ -35,9 +35,11 @@
</member>
<member name="application/provisioning_profile_uuid_debug" type="String" setter="" getter="">
UUID of the provisioning profile. If left empty, Xcode will download or create a provisioning profile automatically. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url].
Can be overridden with the environment variable [code]GODOT_IOS_PROVISIONING_PROFILE_UUID_DEBUG[/code].
</member>
<member name="application/provisioning_profile_uuid_release" type="String" setter="" getter="">
UUID of the provisioning profile. If left empty, Xcode will download or create a provisioning profile automatically. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url].
Can be overridden with the environment variable [code]GODOT_IOS_PROVISIONING_PROFILE_UUID_RELEASE[/code].
</member>
<member name="application/short_version" type="String" setter="" getter="">
Application version visible to the user, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]).

View File

@ -160,10 +160,10 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_store_team_id"), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Developer"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_debug", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 1));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Distribution"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0));
@ -253,8 +253,8 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
};
String dbg_sign_id = p_preset->get("application/code_sign_identity_debug").operator String().is_empty() ? "iPhone Developer" : p_preset->get("application/code_sign_identity_debug");
String rel_sign_id = p_preset->get("application/code_sign_identity_release").operator String().is_empty() ? "iPhone Distribution" : p_preset->get("application/code_sign_identity_release");
bool dbg_manual = !p_preset->get("application/provisioning_profile_uuid_debug").operator String().is_empty() || (dbg_sign_id != "iPhone Developer");
bool rel_manual = !p_preset->get("application/provisioning_profile_uuid_release").operator String().is_empty() || (rel_sign_id != "iPhone Distribution");
bool dbg_manual = !p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG).operator String().is_empty() || (dbg_sign_id != "iPhone Developer");
bool rel_manual = !p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_IOS_PROFILE_UUID_RELEASE).operator String().is_empty() || (rel_sign_id != "iPhone Distribution");
String str;
String strnew;
str.parse_utf8((const char *)pfile.ptr(), pfile.size());
@ -288,9 +288,9 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
int export_method = p_preset->get(p_debug ? "application/export_method_debug" : "application/export_method_release");
strnew += lines[i].replace("$export_method", export_method_string[export_method]) + "\n";
} else if (lines[i].find("$provisioning_profile_uuid_release") != -1) {
strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get("application/provisioning_profile_uuid_release")) + "\n";
strnew += lines[i].replace("$provisioning_profile_uuid_release", p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_IOS_PROFILE_UUID_RELEASE)) + "\n";
} else if (lines[i].find("$provisioning_profile_uuid_debug") != -1) {
strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get("application/provisioning_profile_uuid_debug")) + "\n";
strnew += lines[i].replace("$provisioning_profile_uuid_debug", p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG)) + "\n";
} else if (lines[i].find("$code_sign_style_debug") != -1) {
if (dbg_manual) {
strnew += lines[i].replace("$code_sign_style_debug", "Manual") + "\n";
@ -304,7 +304,7 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_
strnew += lines[i].replace("$code_sign_style_release", "Automatic") + "\n";
}
} else if (lines[i].find("$provisioning_profile_uuid") != -1) {
String uuid = p_debug ? p_preset->get("application/provisioning_profile_uuid_debug") : p_preset->get("application/provisioning_profile_uuid_release");
String uuid = p_debug ? p_preset->get_or_env("application/provisioning_profile_uuid_debug", ENV_IOS_PROFILE_UUID_DEBUG) : p_preset->get_or_env("application/provisioning_profile_uuid_release", ENV_IOS_PROFILE_UUID_RELEASE);
strnew += lines[i].replace("$provisioning_profile_uuid", uuid) + "\n";
} else if (lines[i].find("$code_sign_identity_debug") != -1) {
strnew += lines[i].replace("$code_sign_identity_debug", dbg_sign_id) + "\n";

View File

@ -49,6 +49,11 @@
#include <sys/stat.h>
// Optional environment variables for defining confidential information. If any
// of these is set, they will override the values set in the credentials file.
const String ENV_IOS_PROFILE_UUID_DEBUG = "GODOT_IOS_PROVISIONING_PROFILE_UUID_DEBUG";
const String ENV_IOS_PROFILE_UUID_RELEASE = "GODOT_IOS_PROVISIONING_PROFILE_UUID_RELEASE";
class EditorExportPlatformIOS : public EditorExportPlatform {
GDCLASS(EditorExportPlatformIOS, EditorExportPlatform);

View File

@ -50,9 +50,11 @@
</member>
<member name="codesign/certificate_file" type="String" setter="" getter="">
PKCS #12 certificate file used to sign [code].app[/code] bundle.
Can be overridden with the environment variable [code]GODOT_MACOS_CODESIGN_CERTIFICATE_FILE[/code].
</member>
<member name="codesign/certificate_password" type="String" setter="" getter="">
Password for the certificate file used to sign [code].app[/code] bundle.
Can be overridden with the environment variable [code]GODOT_MACOS_CODESIGN_CERTIFICATE_PASSWORD[/code].
</member>
<member name="codesign/codesign" type="int" setter="" getter="">
Tool to use for code signing.
@ -138,6 +140,7 @@
</member>
<member name="codesign/provisioning_profile" type="String" setter="" getter="">
Provisioning profile file downloaded from Apple developer account dashboard. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url].
Can be overridden with the environment variable [code]GODOT_MACOS_CODESIGN_PROVISIONING_PROFILE[/code].
</member>
<member name="custom_template/debug" type="String" setter="" getter="">
Path to the custom export template. If left empty, default template is used.
@ -156,18 +159,23 @@
</member>
<member name="notarization/api_key" type="String" setter="" getter="">
Apple App Store Connect API issuer key file.
Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_API_KEY[/code].
</member>
<member name="notarization/api_key_id" type="String" setter="" getter="">
Apple App Store Connect API issuer key ID.
Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_API_KEY_ID[/code].
</member>
<member name="notarization/api_uuid" type="String" setter="" getter="">
Apple App Store Connect API issuer UUID.
Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_API_UUID[/code].
</member>
<member name="notarization/apple_id_name" type="String" setter="" getter="">
Apple ID account name (email address).
Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_APPLE_ID_NAME[/code].
</member>
<member name="notarization/apple_id_password" type="String" setter="" getter="">
Apple ID app-specific password.
Can be overridden with the environment variable [code]GODOT_MACOS_NOTARIZATION_APPLE_ID_PASSWORD[/code].
</member>
<member name="notarization/notarization" type="int" setter="" getter="">
Tool to use for notarization.

View File

@ -73,7 +73,7 @@ String EditorExportPlatformMacOS::get_export_option_warning(const EditorExportPr
ad_hoc = true;
} break;
case 2: { // "rcodesign"
ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty();
ad_hoc = p_preset->get_or_env("codesign/certificate_file", ENV_MAC_CODESIGN_CERT_FILE).operator String().is_empty() || p_preset->get_or_env("codesign/certificate_password", ENV_MAC_CODESIGN_CERT_FILE).operator String().is_empty();
} break;
#ifdef MACOS_ENABLED
case 3: { // "codesign"
@ -114,7 +114,7 @@ String EditorExportPlatformMacOS::get_export_option_warning(const EditorExportPr
}
if (p_name == "codesign/provisioning_profile" && dist_type == 2) {
String pprof = p_preset->get("codesign/provisioning_profile");
String pprof = p_preset->get_or_env("codesign/provisioning_profile", ENV_MAC_CODESIGN_PROFILE);
if (pprof.is_empty()) {
return TTR("Provisioning profile is required for App Store distribution.");
}
@ -154,8 +154,8 @@ String EditorExportPlatformMacOS::get_export_option_warning(const EditorExportPr
if (notary_tool == 2 || notary_tool == 3) {
if (p_name == "notarization/apple_id_name" || p_name == "notarization/api_uuid") {
String apple_id = p_preset->get("notarization/apple_id_name");
String api_uuid = p_preset->get("notarization/api_uuid");
String apple_id = p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID);
String api_uuid = p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID);
if (apple_id.is_empty() && api_uuid.is_empty()) {
return TTR("Neither Apple ID name nor App Store Connect issuer ID name not specified.");
}
@ -164,28 +164,28 @@ String EditorExportPlatformMacOS::get_export_option_warning(const EditorExportPr
}
}
if (p_name == "notarization/apple_id_password") {
String apple_id = p_preset->get("notarization/apple_id_name");
String apple_pass = p_preset->get("notarization/apple_id_password");
String apple_id = p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID);
String apple_pass = p_preset->get_or_env("notarization/apple_id_password", ENV_MAC_NOTARIZATION_APPLE_PASS);
if (!apple_id.is_empty() && apple_pass.is_empty()) {
return TTR("Apple ID password not specified.");
}
}
if (p_name == "notarization/api_key_id") {
String api_uuid = p_preset->get("notarization/api_uuid");
String api_key = p_preset->get("notarization/api_key_id");
String api_uuid = p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID);
String api_key = p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID);
if (!api_uuid.is_empty() && api_key.is_empty()) {
return TTR("App Store Connect API key ID not specified.");
}
}
} else if (notary_tool == 1) {
if (p_name == "notarization/api_uuid") {
String api_uuid = p_preset->get("notarization/api_uuid");
String api_uuid = p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID);
if (api_uuid.is_empty()) {
return TTR("App Store Connect issuer ID name not specified.");
}
}
if (p_name == "notarization/api_key_id") {
String api_key = p_preset->get("notarization/api_key_id");
String api_key = p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID);
if (api_key.is_empty()) {
return TTR("App Store Connect API key ID not specified.");
}
@ -398,10 +398,10 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
// "codesign" only options:
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_PLACEHOLDER_TEXT, "Type: Name (ID)"), ""));
// "rcodesign" only options:
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_file", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_password", PROPERTY_HINT_PASSWORD), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_file", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/certificate_password", PROPERTY_HINT_PASSWORD, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
// "codesign" and "rcodesign" only options:
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/provisioning_profile", PROPERTY_HINT_GLOBAL_FILE, "*.provisionprofile"), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/provisioning_profile", PROPERTY_HINT_GLOBAL_FILE, "*.provisionprofile", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/entitlements/custom_file", PROPERTY_HINT_GLOBAL_FILE, "*.plist"), "", true));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/entitlements/allow_jit_code_execution"), false));
@ -434,12 +434,12 @@ void EditorExportPlatformMacOS::get_export_options(List<ExportOption> *r_options
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "notarization/notarization", PROPERTY_HINT_ENUM, "Disabled,rcodesign"), 0, true));
#endif
// "altool" and "notarytool" only options:
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email"), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PASSWORD, "Enable two-factor authentication and provide app-specific password"), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Apple ID email", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/apple_id_password", PROPERTY_HINT_PASSWORD, "Enable two-factor authentication and provide app-specific password", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true));
// "altool", "notarytool" and "rcodesign" only options:
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_uuid", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect issuer ID UUID"), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key", PROPERTY_HINT_GLOBAL_FILE, "*.p8"), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect API key ID"), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_uuid", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect issuer ID UUID", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key", PROPERTY_HINT_GLOBAL_FILE, "*.p8", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "notarization/api_key_id", PROPERTY_HINT_PLACEHOLDER_TEXT, "App Store Connect API key ID", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "privacy/microphone_usage_description", PROPERTY_HINT_PLACEHOLDER_TEXT, "Provide a message if you need to use the microphone"), "", false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::DICTIONARY, "privacy/microphone_usage_description_localized", PROPERTY_HINT_LOCALIZABLE_STRING), Dictionary()));
@ -776,24 +776,24 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres
args.push_back("notary-submit");
if (p_preset->get("notarization/api_uuid") == "") {
if (p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect issuer ID name not specified."));
return Error::FAILED;
}
if (p_preset->get("notarization/api_key") == "") {
if (p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect API key ID not specified."));
return Error::FAILED;
}
args.push_back("--api-issuer");
args.push_back(p_preset->get("notarization/api_uuid"));
args.push_back(p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID));
args.push_back("--api-key");
args.push_back(p_preset->get("notarization/api_key_id"));
args.push_back(p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID));
if (!p_preset->get("notarization/api_key").operator String().is_empty()) {
if (!p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY).operator String().is_empty()) {
args.push_back("--api-key-path");
args.push_back(p_preset->get("notarization/api_key"));
args.push_back(p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY));
}
args.push_back(p_path);
@ -840,40 +840,40 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres
args.push_back(p_path);
if (p_preset->get("notarization/apple_id_name") == "" && p_preset->get("notarization/api_uuid") == "") {
if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) == "" && p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Neither Apple ID name nor App Store Connect issuer ID name not specified."));
return Error::FAILED;
}
if (p_preset->get("notarization/apple_id_name") != "" && p_preset->get("notarization/api_uuid") != "") {
if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) != "" && p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID) != "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Both Apple ID name and App Store Connect issuer ID name are specified, only one should be set at the same time."));
return Error::FAILED;
}
if (p_preset->get("notarization/apple_id_name") != "") {
if (p_preset->get("notarization/apple_id_password") == "") {
if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) != "") {
if (p_preset->get_or_env("notarization/apple_id_password", ENV_MAC_NOTARIZATION_APPLE_PASS) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Apple ID password not specified."));
return Error::FAILED;
}
args.push_back("--apple-id");
args.push_back(p_preset->get("notarization/apple_id_name"));
args.push_back(p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID));
args.push_back("--password");
args.push_back(p_preset->get("notarization/apple_id_password"));
args.push_back(p_preset->get_or_env("notarization/apple_id_password", ENV_MAC_NOTARIZATION_APPLE_PASS));
} else {
if (p_preset->get("notarization/api_key_id") == "") {
if (p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect API key ID not specified."));
return Error::FAILED;
}
args.push_back("--issuer");
args.push_back(p_preset->get("notarization/api_uuid"));
args.push_back(p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID));
if (!p_preset->get("notarization/api_key").operator String().is_empty()) {
if (!p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY).operator String().is_empty()) {
args.push_back("--key");
args.push_back(p_preset->get("notarization/api_key"));
args.push_back(p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY));
}
args.push_back("--key-id");
args.push_back(p_preset->get("notarization/api_key_id"));
args.push_back(p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID));
}
args.push_back("--no-progress");
@ -925,35 +925,35 @@ Error EditorExportPlatformMacOS::_notarize(const Ref<EditorExportPreset> &p_pres
args.push_back("--primary-bundle-id");
args.push_back(p_preset->get("application/bundle_identifier"));
if (p_preset->get("notarization/apple_id_name") == "" && p_preset->get("notarization/api_uuid") == "") {
if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) == "" && p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Neither Apple ID name nor App Store Connect issuer ID name not specified."));
return Error::FAILED;
}
if (p_preset->get("notarization/apple_id_name") != "" && p_preset->get("notarization/api_uuid") != "") {
if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) != "" && p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID) != "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Both Apple ID name and App Store Connect issuer ID name are specified, only one should be set at the same time."));
return Error::FAILED;
}
if (p_preset->get("notarization/apple_id_name") != "") {
if (p_preset->get("notarization/apple_id_password") == "") {
if (p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID) != "") {
if (p_preset->get_or_env("notarization/apple_id_password", ENV_MAC_NOTARIZATION_APPLE_PASS) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("Apple ID password not specified."));
return Error::FAILED;
}
args.push_back("--username");
args.push_back(p_preset->get("notarization/apple_id_name"));
args.push_back(p_preset->get_or_env("notarization/apple_id_name", ENV_MAC_NOTARIZATION_APPLE_ID));
args.push_back("--password");
args.push_back(p_preset->get("notarization/apple_id_password"));
args.push_back(p_preset->get_or_env("notarization/apple_id_password", ENV_MAC_NOTARIZATION_APPLE_PASS));
} else {
if (p_preset->get("notarization/api_key") == "") {
if (p_preset->get_or_env("notarization/api_key", ENV_MAC_NOTARIZATION_KEY) == "") {
add_message(EXPORT_MESSAGE_ERROR, TTR("Notarization"), TTR("App Store Connect API key ID not specified."));
return Error::FAILED;
}
args.push_back("--apiIssuer");
args.push_back(p_preset->get("notarization/api_uuid"));
args.push_back(p_preset->get_or_env("notarization/api_uuid", ENV_MAC_NOTARIZATION_UUID));
args.push_back("--apiKey");
args.push_back(p_preset->get("notarization/api_key_id"));
args.push_back(p_preset->get_or_env("notarization/api_key_id", ENV_MAC_NOTARIZATION_KEY_ID));
}
args.push_back("--type");
@ -1032,8 +1032,8 @@ Error EditorExportPlatformMacOS::_code_sign(const Ref<EditorExportPreset> &p_pre
args.push_back(p_ent_path);
}
String certificate_file = p_preset->get("codesign/certificate_file");
String certificate_pass = p_preset->get("codesign/certificate_password");
String certificate_file = p_preset->get_or_env("codesign/certificate_file", ENV_MAC_CODESIGN_CERT_FILE);
String certificate_pass = p_preset->get_or_env("codesign/certificate_password", ENV_MAC_CODESIGN_CERT_PASS);
if (!certificate_file.is_empty() && !certificate_pass.is_empty()) {
args.push_back("--p12-file");
args.push_back(certificate_file);
@ -1763,7 +1763,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
ad_hoc = true;
} break;
case 2: { // "rcodesign"
ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty();
ad_hoc = p_preset->get_or_env("codesign/certificate_file", ENV_MAC_CODESIGN_CERT_FILE).operator String().is_empty() || p_preset->get_or_env("codesign/certificate_password", ENV_MAC_CODESIGN_CERT_PASS).operator String().is_empty();
} break;
#ifdef MACOS_ENABLED
case 3: { // "codesign"
@ -1857,7 +1857,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
int dist_type = p_preset->get("export/distribution_type");
if (dist_type == 2) {
String pprof = p_preset->get("codesign/provisioning_profile");
String pprof = p_preset->get_or_env("codesign/provisioning_profile", ENV_MAC_CODESIGN_PROFILE);
String teamid = p_preset->get("codesign/apple_team_id");
String bid = p_preset->get("application/bundle_identifier");
if (!pprof.is_empty() && !teamid.is_empty()) {
@ -1990,7 +1990,7 @@ Error EditorExportPlatformMacOS::export_project(const Ref<EditorExportPreset> &p
if (err == OK && sign_enabled) {
int dist_type = p_preset->get("export/distribution_type");
if (dist_type == 2) {
String pprof = p_preset->get("codesign/provisioning_profile").operator String();
String pprof = p_preset->get_or_env("codesign/provisioning_profile", ENV_MAC_CODESIGN_PROFILE).operator String();
if (!pprof.is_empty()) {
Ref<DirAccess> da = DirAccess::create(DirAccess::ACCESS_FILESYSTEM);
err = da->copy(pprof, tmp_app_path_name + "/Contents/embedded.provisionprofile");
@ -2147,7 +2147,7 @@ bool EditorExportPlatformMacOS::has_valid_project_configuration(const Ref<Editor
ad_hoc = true;
} break;
case 2: { // "rcodesign"
ad_hoc = p_preset->get("codesign/certificate_file").operator String().is_empty() || p_preset->get("codesign/certificate_password").operator String().is_empty();
ad_hoc = p_preset->get_or_env("codesign/certificate_file", ENV_MAC_CODESIGN_CERT_FILE).operator String().is_empty() || p_preset->get_or_env("codesign/certificate_password", ENV_MAC_CODESIGN_CERT_PASS).operator String().is_empty();
} break;
#ifdef MACOS_ENABLED
case 3: { // "codesign"

View File

@ -43,6 +43,17 @@
#include <sys/stat.h>
// Optional environment variables for defining confidential information. If any
// of these is set, they will override the values set in the credentials file.
const String ENV_MAC_CODESIGN_CERT_FILE = "GODOT_MACOS_CODESIGN_CERTIFICATE_FILE";
const String ENV_MAC_CODESIGN_CERT_PASS = "GODOT_MACOS_CODESIGN_CERTIFICATE_PASSWORD";
const String ENV_MAC_CODESIGN_PROFILE = "GODOT_MACOS_CODESIGN_PROVISIONING_PROFILE";
const String ENV_MAC_NOTARIZATION_UUID = "GODOT_MACOS_NOTARIZATION_API_UUID";
const String ENV_MAC_NOTARIZATION_KEY = "GODOT_MACOS_NOTARIZATION_API_KEY";
const String ENV_MAC_NOTARIZATION_KEY_ID = "GODOT_MACOS_NOTARIZATION_API_KEY_ID";
const String ENV_MAC_NOTARIZATION_APPLE_ID = "GODOT_MACOS_NOTARIZATION_APPLE_ID_NAME";
const String ENV_MAC_NOTARIZATION_APPLE_PASS = "GODOT_MACOS_NOTARIZATION_APPLE_ID_PASSWORD";
class EditorExportPlatformMacOS : public EditorExportPlatform {
GDCLASS(EditorExportPlatformMacOS, EditorExportPlatform);

View File

@ -80,8 +80,8 @@ void EditorExportPlatformUWP::get_export_options(List<ExportOption> *r_options)
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/product_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/publisher_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/certificate", PROPERTY_HINT_GLOBAL_FILE, "*.pfx"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/password"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/certificate", PROPERTY_HINT_GLOBAL_FILE, "*.pfx", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "signing/algorithm", PROPERTY_HINT_ENUM, "MD5,SHA1,SHA256"), 2));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/major"), 1));
@ -465,8 +465,8 @@ Error EditorExportPlatformUWP::export_project(const Ref<EditorExportPreset> &p_p
int cert_alg = EDITOR_GET("export/uwp/debug_algorithm");
if (!p_debug) {
cert_path = p_preset->get("signing/certificate");
cert_pass = p_preset->get("signing/password");
cert_path = p_preset->get_or_env("signing/certificate", ENV_UWP_SIGNING_CERT);
cert_pass = p_preset->get_or_env("signing/password", ENV_UWP_SIGNING_PASS);
cert_alg = p_preset->get("signing/algorithm");
}

View File

@ -85,6 +85,11 @@ static const char *uwp_device_capabilities[] = {
nullptr
};
// Optional environment variables for defining confidential information. If any
// of these is set, they will override the values set in the credentials file.
const String ENV_UWP_SIGNING_CERT = "GODOT_UWP_SIGNING_CERTIFICATE";
const String ENV_UWP_SIGNING_PASS = "GODOT_UWP_SIGNING_PASSWORD";
class EditorExportPlatformUWP : public EditorExportPlatform {
GDCLASS(EditorExportPlatformUWP, EditorExportPlatform);

View File

@ -64,12 +64,15 @@
</member>
<member name="codesign/identity" type="String" setter="" getter="">
PKCS #12 certificate file used to sign executable or certificate SHA-1 hash (if [member codesign/identity_type] is set to "Use certificate store"). See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].
Can be overridden with the environment variable [code]GODOT_WINDOWS_CODESIGN_IDENTITY[/code].
</member>
<member name="codesign/identity_type" type="int" setter="" getter="">
Type of identity to use. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].
Can be overridden with the environment variable [code]GODOT_WINDOWS_CODESIGN_IDENTITY_TYPE[/code].
</member>
<member name="codesign/password" type="String" setter="" getter="">
Password for the certificate file used to sign executable. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].
Can be overridden with the environment variable [code]GODOT_WINDOWS_CODESIGN_PASSWORD[/code].
</member>
<member name="codesign/timestamp" type="bool" setter="" getter="">
If [code]true[/code], time-stamp is added to the signature. See [url=https://learn.microsoft.com/en-us/dotnet/framework/tools/signtool-exe]Sign Tool[/url].

View File

@ -328,9 +328,9 @@ void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_optio
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "binary_format/architecture", PROPERTY_HINT_ENUM, "x86_64,x86_32,arm64"), "x86_64"));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/enable"), false, true));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA-1 hash)"), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password", PROPERTY_HINT_PASSWORD), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/identity_type", PROPERTY_HINT_ENUM, "Select automatically,Use PKCS12 file (specify *.PFX/*.P12 file),Use certificate store (specify SHA-1 hash)", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), 0));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/identity", PROPERTY_HINT_GLOBAL_FILE, "*.pfx,*.p12", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/password", PROPERTY_HINT_PASSWORD, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "codesign/timestamp"), true));
r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "codesign/timestamp_server_url"), ""));
r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "codesign/digest_algorithm", PROPERTY_HINT_ENUM, "SHA1,SHA256"), 1));
@ -518,21 +518,21 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
//identity
#ifdef WINDOWS_ENABLED
int id_type = p_preset->get("codesign/identity_type");
int id_type = p_preset->get_or_env("codesign/identity_type", ENV_WIN_CODESIGN_ID_TYPE);
if (id_type == 0) { //auto select
args.push_back("/a");
} else if (id_type == 1) { //pkcs12
if (p_preset->get("codesign/identity") != "") {
if (p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID) != "") {
args.push_back("/f");
args.push_back(p_preset->get("codesign/identity"));
args.push_back(p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID));
} else {
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found."));
return FAILED;
}
} else if (id_type == 2) { //Windows certificate store
if (p_preset->get("codesign/identity") != "") {
if (p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID) != "") {
args.push_back("/sha1");
args.push_back(p_preset->get("codesign/identity"));
args.push_back(p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID));
} else {
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found."));
return FAILED;
@ -543,9 +543,9 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
}
#else
int id_type = 1;
if (p_preset->get("codesign/identity") != "") {
if (p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID) != "") {
args.push_back("-pkcs12");
args.push_back(p_preset->get("codesign/identity"));
args.push_back(p_preset->get_or_env("codesign/identity", ENV_WIN_CODESIGN_ID));
} else {
add_message(EXPORT_MESSAGE_WARNING, TTR("Code Signing"), TTR("No identity found."));
return FAILED;
@ -553,13 +553,13 @@ Error EditorExportPlatformWindows::_code_sign(const Ref<EditorExportPreset> &p_p
#endif
//password
if ((id_type == 1) && (p_preset->get("codesign/password") != "")) {
if ((id_type == 1) && (p_preset->get_or_env("codesign/password", ENV_WIN_CODESIGN_PASS) != "")) {
#ifdef WINDOWS_ENABLED
args.push_back("/p");
#else
args.push_back("-pass");
#endif
args.push_back(p_preset->get("codesign/password"));
args.push_back(p_preset->get_or_env("codesign/password", ENV_WIN_CODESIGN_PASS));
}
//timestamp

View File

@ -36,6 +36,12 @@
#include "editor/editor_settings.h"
#include "editor/export/editor_export_platform_pc.h"
// Optional environment variables for defining confidential information. If any
// of these is set, they will override the values set in the credentials file.
const String ENV_WIN_CODESIGN_ID_TYPE = "GODOT_WINDOWS_CODESIGN_IDENTITY_TYPE";
const String ENV_WIN_CODESIGN_ID = "GODOT_WINDOWS_CODESIGN_IDENTITY";
const String ENV_WIN_CODESIGN_PASS = "GODOT_WINDOWS_CODESIGN_PASSWORD";
class EditorExportPlatformWindows : public EditorExportPlatformPC {
GDCLASS(EditorExportPlatformWindows, EditorExportPlatformPC);