mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-05 00:15:32 +00:00
Merge branch 'master' into chore/ios/additional-logging-for-xcodebuild
This commit is contained in:
commit
43756e5e78
22 changed files with 399 additions and 54 deletions
|
|
@ -1,5 +1,11 @@
|
|||
# Keyman Version History
|
||||
|
||||
## 19.0.234 alpha 2026-05-19
|
||||
|
||||
* chore(deps): bump brace-expansion from 5.0.5 to 5.0.6 in /developer/src/server/src/win32/trayicon/addon-src (#15969)
|
||||
* chore(deps): bump ws from 8.18.1 to 8.20.1 (#15971)
|
||||
* chore: move to .localhost for local server URLs (#15963)
|
||||
|
||||
## 19.0.233 alpha 2026-05-18
|
||||
|
||||
* chore(deps): bump path-to-regexp and express (#15902)
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
19.0.234
|
||||
19.0.235
|
||||
|
|
@ -66,11 +66,11 @@ const
|
|||
URLPath_Community = '/go/'+SKeymanVersion+'/community';
|
||||
|
||||
// Keyboard download and installation
|
||||
URLPath_RegEx_MatchKeyboardsInstall = '^http(?:s)?://keyman(?:-staging)?\.com(?:\.local)?/keyboards/install/([^?/]+)(?:\?(.+))?$';
|
||||
URLPath_RegEx_MatchKeyboardsInstall = '^http(?:s)?://keyman(?:-staging)?\.com(?:\.localhost)?/keyboards/install/([^?/]+)(?:\?(.+))?$';
|
||||
// e.g. https://keyman.com/keyboards/install/foo
|
||||
UrlPath_RegEx_MatchKeyboardsRoot = '^http(?:s)?://keyman(?:-staging)?\.com(?:\.local)?/keyboards([/?].*)?$';
|
||||
// e.g. http://keyman.com.local/keyboards/foo
|
||||
UrlPath_RegEx_MatchKeyboardsGo = '^http(?:s)?://keyman(?:-staging)?\.com(?:\.local)?/go/windows/[^/]+/download-keyboards';
|
||||
UrlPath_RegEx_MatchKeyboardsRoot = '^http(?:s)?://keyman(?:-staging)?\.com(?:\.localhost)?/keyboards([/?].*)?$';
|
||||
// e.g. http://keyman.com.localhost/keyboards/foo
|
||||
UrlPath_RegEx_MatchKeyboardsGo = '^http(?:s)?://keyman(?:-staging)?\.com(?:\.localhost)?/go/windows/[^/]+/download-keyboards';
|
||||
// e.g. https://keyman-staging.com/go/windows/14.0/download-keyboards?version=14.0.146.0
|
||||
|
||||
// Cloning keyboards - Keyman Developer
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
|
||||
|
||||
#include "action.hpp"
|
||||
|
|
@ -183,3 +184,34 @@ bool km::core::state::set_actions(
|
|||
|
||||
return true;
|
||||
}
|
||||
using namespace km::core;
|
||||
|
||||
namespace {
|
||||
|
||||
km_core_usv * duplicate_km_core_usv(const km_core_usv *src) {
|
||||
if (!src) {
|
||||
return nullptr;
|
||||
}
|
||||
size_t len = 0;
|
||||
while (src[len]) {
|
||||
++len;
|
||||
}
|
||||
km_core_usv *result = new km_core_usv[len + 1];
|
||||
std::copy(src, src + len + 1, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
km_core_actions km::core::clone_actions_object(km_core_actions const &src) {
|
||||
km_core_actions result;
|
||||
memset(&result, 0, sizeof(result));
|
||||
result.code_points_to_delete = src.code_points_to_delete;
|
||||
result.do_alert = src.do_alert;
|
||||
result.emit_keystroke = src.emit_keystroke;
|
||||
result.new_caps_lock_state = src.new_caps_lock_state;
|
||||
result.deleted_context = duplicate_km_core_usv(src.deleted_context);
|
||||
result.output = duplicate_km_core_usv(src.output);
|
||||
result.persist_options = clone_options(src.persist_options);
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,5 +39,9 @@ namespace core
|
|||
unsigned int code_points_to_delete
|
||||
);
|
||||
|
||||
km_core_actions clone_actions_object(
|
||||
km_core_actions const &src
|
||||
);
|
||||
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -37,7 +37,9 @@ km_core_event(
|
|||
return KM_CORE_STATUS_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
return state->processor().external_event(state, event, data);
|
||||
km_core_status status = state->processor().external_event(state, event, data);
|
||||
state->apply_actions_and_merge_app_context();
|
||||
return status;
|
||||
}
|
||||
|
||||
km_core_status
|
||||
|
|
|
|||
|
|
@ -82,3 +82,18 @@ json & km::core::operator << (json &j, abstract_processor const &)
|
|||
|
||||
return j;
|
||||
}
|
||||
|
||||
km_core_option_item *
|
||||
km::core::clone_options(km_core_option_item const *src) {
|
||||
if (!src) {
|
||||
return nullptr;
|
||||
}
|
||||
size_t count = km_core_options_list_size(src);
|
||||
km_core_option_item *result = new km_core_option_item[count + 1];
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
km::core::option opt(static_cast<km_core_option_scope>(src[i].scope), src[i].key, src[i].value);
|
||||
result[i] = opt.release();
|
||||
}
|
||||
result[count] = KM_CORE_OPTIONS_END;
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ namespace core
|
|||
return key == nullptr;
|
||||
}
|
||||
|
||||
|
||||
km_core_option_item * clone_options(km_core_option_item const *src);
|
||||
|
||||
} // namespace core
|
||||
} // namespace km
|
||||
|
|
|
|||
|
|
@ -56,6 +56,9 @@ state::state(km::core::abstract_processor & ap, km_core_option_item const *env)
|
|||
_imx_callback = nullptr;
|
||||
_imx_object = nullptr;
|
||||
memset(const_cast<km_core_actions*>(&_action_struct), 0, sizeof(km_core_actions));
|
||||
// Ensure _action_struct is initialized to the default values
|
||||
km_core_action_item no_actions = {KM_CORE_IT_END, {0,}, {0}};
|
||||
action_item_list_to_actions_object(&no_actions, &this->_action_struct);
|
||||
}
|
||||
|
||||
void state::imx_register_callback(
|
||||
|
|
@ -82,6 +85,19 @@ void state::imx_callback(uint32_t imx_id) {
|
|||
_imx_callback(static_cast<km_core_state *>(this), imx_id, _imx_object);
|
||||
}
|
||||
|
||||
state::state(state const &other)
|
||||
: _ctxt(other._ctxt)
|
||||
, _app_ctxt(other._app_ctxt)
|
||||
, _processor(other._processor)
|
||||
, _actions(other._actions)
|
||||
, _action_struct(clone_actions_object(other._action_struct))
|
||||
, _debug_items(other._debug_items)
|
||||
, _imx_callback(other._imx_callback)
|
||||
, _imx_object(other._imx_object)
|
||||
, _backspace_handled_internally(other._backspace_handled_internally)
|
||||
{
|
||||
}
|
||||
|
||||
state::~state() {
|
||||
km::core::actions_dispose(this->_action_struct);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ protected:
|
|||
public:
|
||||
state(core::abstract_processor & kb, km_core_option_item const *env);
|
||||
|
||||
state(state const &) = default;
|
||||
state(state const &other);
|
||||
state(state const &&) = delete;
|
||||
|
||||
~state();
|
||||
|
|
|
|||
|
|
@ -42,6 +42,165 @@ namespace
|
|||
return buf;
|
||||
}
|
||||
|
||||
inline
|
||||
bool action_options_equal(km_core_option_item const * lhs,
|
||||
km_core_option_item const * rhs)
|
||||
{
|
||||
if (lhs == rhs) return true;
|
||||
if (!lhs || !rhs) return false;
|
||||
|
||||
while (lhs->key && rhs->key) {
|
||||
if (lhs->scope != rhs->scope) return false;
|
||||
if (std::u16string(lhs->key) != std::u16string(rhs->key)) return false;
|
||||
if (std::u16string(lhs->value) != std::u16string(rhs->value)) return false;
|
||||
++lhs;
|
||||
++rhs;
|
||||
}
|
||||
|
||||
return lhs->key == nullptr && rhs->key == nullptr;
|
||||
}
|
||||
|
||||
inline
|
||||
bool expect_action_struct(
|
||||
km_core_actions const & actions,
|
||||
unsigned int expected_code_points_to_delete,
|
||||
km_core_usv const * expected_output,
|
||||
km_core_option_item const * expected_persist_options,
|
||||
km_core_bool expected_do_alert,
|
||||
km_core_bool expected_emit_keystroke,
|
||||
km_core_caps_state expected_new_caps_lock_state,
|
||||
km_core_usv const * expected_deleted_context
|
||||
) {
|
||||
bool all_passed = true;
|
||||
|
||||
std::cout << "\n=== Comparing action_struct fields ===" << std::endl;
|
||||
|
||||
// code_points_to_delete
|
||||
std::cout << "code_points_to_delete: " << actions.code_points_to_delete
|
||||
<< " (expected: " << expected_code_points_to_delete << ")";
|
||||
if (actions.code_points_to_delete != expected_code_points_to_delete) {
|
||||
std::cout << " [FAIL]" << std::endl;
|
||||
all_passed = false;
|
||||
} else {
|
||||
std::cout << " [PASS]" << std::endl;
|
||||
}
|
||||
|
||||
// output
|
||||
std::cout << "output: " << (actions.output ? std::u32string(actions.output) : U"(null)")
|
||||
<< " expected: " << (expected_output ? std::u32string(expected_output) : U"(null)") << std::endl;
|
||||
bool output_equal = false;
|
||||
if (expected_output == actions.output) { // nullptr or same pointer
|
||||
output_equal = true;
|
||||
} else if (!expected_output || !actions.output) {
|
||||
output_equal = false;
|
||||
} else if (expected_output && actions.output) {
|
||||
if (std::u32string(actions.output) != std::u32string(expected_output)) {
|
||||
output_equal = false;
|
||||
} else {
|
||||
output_equal = true;
|
||||
}
|
||||
}
|
||||
if (!output_equal) {
|
||||
std::cout << " [FAIL]" << std::endl;
|
||||
all_passed = false;
|
||||
} else {
|
||||
std::cout << " [PASS]" << std::endl;
|
||||
}
|
||||
|
||||
// persist_options
|
||||
std::cout << "persist_options comparison:" << std::endl;
|
||||
if (!action_options_equal(actions.persist_options, expected_persist_options)) {
|
||||
std::cout << " actual: ";
|
||||
if (actions.persist_options) {
|
||||
for (auto opt = actions.persist_options; opt->scope; ++opt) {
|
||||
std::cout << "[scope=" << opt->scope << ", key=" << opt->key << ", value=" << opt->value << "] ";
|
||||
}
|
||||
} else {
|
||||
std::cout << "(null)";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
std::cout << " expected: ";
|
||||
if (expected_persist_options) {
|
||||
for (auto opt = expected_persist_options; opt->key; ++opt) {
|
||||
std::cout << "[scope=" << opt->scope << ", key=" << opt->key << ", value=" << opt->value << "] ";
|
||||
}
|
||||
} else {
|
||||
std::cout << "(null)";
|
||||
}
|
||||
std::cout << " [FAIL]" << std::endl;
|
||||
all_passed = false;
|
||||
} else {
|
||||
std::cout << " [PASS]" << std::endl;
|
||||
}
|
||||
|
||||
// do_alert
|
||||
std::cout << "do_alert: " << (int)actions.do_alert
|
||||
<< " (expected: " << (int)expected_do_alert << ")";
|
||||
if (actions.do_alert != expected_do_alert) {
|
||||
std::cout << " [FAIL]" << std::endl;
|
||||
all_passed = false;
|
||||
} else {
|
||||
std::cout << " [PASS]" << std::endl;
|
||||
}
|
||||
|
||||
// emit_keystroke
|
||||
std::cout << "emit_keystroke: " << (int)actions.emit_keystroke
|
||||
<< " (expected: " << (int)expected_emit_keystroke << ")";
|
||||
if (actions.emit_keystroke != expected_emit_keystroke) {
|
||||
std::cout << " [FAIL]" << std::endl;
|
||||
all_passed = false;
|
||||
} else {
|
||||
std::cout << " [PASS]" << std::endl;
|
||||
}
|
||||
|
||||
// new_caps_lock_state
|
||||
std::cout << "new_caps_lock_state: " << (int)actions.new_caps_lock_state
|
||||
<< " (expected: " << (int)expected_new_caps_lock_state << ")";
|
||||
if (actions.new_caps_lock_state != expected_new_caps_lock_state) {
|
||||
std::cout << " [FAIL]" << std::endl;
|
||||
all_passed = false;
|
||||
} else {
|
||||
std::cout << " [PASS]" << std::endl;
|
||||
}
|
||||
|
||||
// deleted_context
|
||||
std::cout << "deleted_context: " << (actions.deleted_context ? std::u32string(actions.deleted_context) : U"(null)")
|
||||
<< " (expected: " << (expected_deleted_context ? std::u32string(expected_deleted_context) : U"(null)") << ")";
|
||||
if (expected_deleted_context != actions.deleted_context) {
|
||||
if (std::u32string(actions.deleted_context) != std::u32string(expected_deleted_context)) {
|
||||
std::cout << " [FAIL]" << std::endl;
|
||||
all_passed = false;
|
||||
} else {
|
||||
std::cout << " [PASS]" << std::endl;
|
||||
}
|
||||
} else {
|
||||
std::cout << " [PASS]" << std::endl;
|
||||
}
|
||||
bool deleted_equal = false;
|
||||
if (expected_deleted_context == actions.deleted_context) { // nullptr or same pointer
|
||||
deleted_equal = true;
|
||||
} else if (!expected_deleted_context || !actions.deleted_context) {
|
||||
deleted_equal = false;
|
||||
} else if (expected_deleted_context && actions.deleted_context) {
|
||||
if (std::u32string(actions.deleted_context) != std::u32string(expected_deleted_context)) {
|
||||
deleted_equal = false;
|
||||
} else {
|
||||
deleted_equal = true;
|
||||
}
|
||||
}
|
||||
if (!deleted_equal) {
|
||||
std::cout << " [FAIL]" << std::endl;
|
||||
all_passed = false;
|
||||
} else {
|
||||
std::cout << " [PASS]" << std::endl;
|
||||
}
|
||||
|
||||
|
||||
std::cout << "=== End comparison ===" << std::endl << std::endl;
|
||||
|
||||
return all_passed;
|
||||
}
|
||||
|
||||
km_core_option_item test_env_opts[] =
|
||||
{
|
||||
{u"hello", u"world", 0},
|
||||
|
|
@ -168,6 +327,13 @@ int main(int argc, char * argv[])
|
|||
try_status(km_core_process_event(test_state, KM_CORE_VKEY_L,
|
||||
KM_CORE_MODIFIER_SHIFT, 1, KM_CORE_EVENT_FLAG_DEFAULT));
|
||||
test_assert(action_items(test_state, {{KM_CORE_IT_CHAR, {0,}, {km_core_usv('L')}}, {KM_CORE_IT_END}}));
|
||||
|
||||
// Without the calling `km_core_state_context_set_if_needed` action struct has a delete for 'L'?
|
||||
// Issue raised to investigate further: https://github.com/keymanapp/keyman/issues/15962
|
||||
km_core_cu const *state_context = get_context_as_string(km_core_state_context(test_state));
|
||||
km_core_state_context_set_if_needed(test_state, state_context);
|
||||
delete [] state_context;
|
||||
|
||||
try_status(km_core_process_event(test_state, KM_CORE_VKEY_F2, 0, 1, KM_CORE_EVENT_FLAG_DEFAULT));
|
||||
|
||||
km_core_action_item action = {KM_CORE_IT_PERSIST_OPT, {0,}, };
|
||||
|
|
@ -187,11 +353,51 @@ int main(int argc, char * argv[])
|
|||
if (doc1 != doc1_expected) return __LINE__;
|
||||
if (doc2 != doc2_expected) return __LINE__;
|
||||
|
||||
// Destroy them
|
||||
km_core_state_dispose(test_state);
|
||||
km_core_state_dispose(test_clone);
|
||||
km_core_keyboard_dispose(test_kb);
|
||||
// Test the action_struct values for the active and cloned states are
|
||||
// independent and match their respective expected values.
|
||||
const unsigned int expected_state_code_points_to_delete = 0;
|
||||
const km_core_usv * expected_state_output = U"";
|
||||
const km_core_bool expected_state_do_alert = KM_CORE_FALSE;
|
||||
const km_core_bool expected_state_emit_keystroke = KM_CORE_FALSE;
|
||||
const km_core_caps_state expected_state_new_caps_lock_state = KM_CORE_CAPS_UNCHANGED;
|
||||
km_core_option_item expected_options[] = {expected_persist_opt, KM_CORE_OPTIONS_END };
|
||||
const km_core_usv * expected_deleted_text = U"";
|
||||
// Cloned expected values
|
||||
const unsigned int clone_state_code_points_to_delete = 0;
|
||||
const km_core_usv * clone_state_output = U"";
|
||||
const km_core_bool clone_state_do_alert = KM_CORE_FALSE;
|
||||
const km_core_bool clone_state_emit_keystroke = KM_CORE_FALSE;
|
||||
const km_core_caps_state clone_state_new_caps_lock_state = KM_CORE_CAPS_UNCHANGED;
|
||||
km_core_option_item clone_state_options[] = {KM_CORE_OPTIONS_END};
|
||||
const km_core_usv * clone_state_deleted_text = nullptr;
|
||||
|
||||
const auto & state_actions = test_state->action_struct();
|
||||
const auto & clone_actions = test_clone->action_struct();
|
||||
|
||||
test_assert (expect_action_struct(state_actions,
|
||||
expected_state_code_points_to_delete,
|
||||
expected_state_output,
|
||||
expected_options,
|
||||
expected_state_do_alert,
|
||||
expected_state_emit_keystroke,
|
||||
expected_state_new_caps_lock_state,
|
||||
expected_deleted_text
|
||||
));
|
||||
|
||||
test_assert (expect_action_struct(clone_actions,
|
||||
clone_state_code_points_to_delete,
|
||||
clone_state_output,
|
||||
clone_state_options,
|
||||
clone_state_do_alert,
|
||||
clone_state_emit_keystroke,
|
||||
clone_state_new_caps_lock_state,
|
||||
clone_state_deleted_text
|
||||
));
|
||||
|
||||
// Destroy them
|
||||
km_core_state_dispose(test_state);
|
||||
km_core_state_dispose(test_clone);
|
||||
km_core_keyboard_dispose(test_kb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
"restructure": "^3.0.1",
|
||||
"sax": ">=0.6.0",
|
||||
"semver": "^7.5.4",
|
||||
"ws": "^8.17.1",
|
||||
"ws": "^8.20.1",
|
||||
"xmlbuilder": "~11.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import Foundation
|
|||
*/
|
||||
public enum KeymanHosts {
|
||||
/**
|
||||
* Used to enable '.local' variants of the endpoints for use in local development testing.
|
||||
* Used to enable '.localhost' variants of the endpoints for use in local development testing.
|
||||
*/
|
||||
internal static let useLocal = false
|
||||
|
||||
|
|
@ -24,7 +24,7 @@ public enum KeymanHosts {
|
|||
// save for use in automated testing.
|
||||
internal static func getApiSiteURL(forTier: Version.Tier, useLocal: Bool) -> URL {
|
||||
if useLocal {
|
||||
return URL.init(string: "http://api.keyman.com.local")!
|
||||
return URL.init(string: "http://api.keyman.com.localhost")!
|
||||
} else {
|
||||
switch forTier {
|
||||
case .alpha:
|
||||
|
|
@ -48,7 +48,7 @@ public enum KeymanHosts {
|
|||
// save for use in automated testing.
|
||||
internal static func getHelpSiteURL(forTier: Version.Tier, useLocal: Bool) -> URL {
|
||||
if useLocal {
|
||||
return URL.init(string: "http://help.keyman.com.local")!
|
||||
return URL.init(string: "http://help.keyman.com.localhost")!
|
||||
} else {
|
||||
switch forTier {
|
||||
case .alpha:
|
||||
|
|
@ -72,7 +72,7 @@ public enum KeymanHosts {
|
|||
// save for use in automated testing.
|
||||
internal static func getMainSiteURL(forTier: Version.Tier, useLocal: Bool) -> URL {
|
||||
if useLocal {
|
||||
return URL.init(string: "http://keyman.com.local")!
|
||||
return URL.init(string: "http://keyman.com.localhost")!
|
||||
} else {
|
||||
switch forTier {
|
||||
case .alpha:
|
||||
|
|
|
|||
|
|
@ -25,11 +25,11 @@ public class UniversalLinks {
|
|||
public static var externalLinkLauncher: ((URL) -> Void)? = nil
|
||||
|
||||
// e.g. https://keyman.com/keyboards/install/foo
|
||||
private static let KEYBOARD_INSTALL_LINK_REGEX = try! NSRegularExpression(pattern: "^http(?:s)?://keyman(?:-staging)?\\.com(?:\\.local)?/keyboards/install/([^?/]+)(?:\\?(.+))?$")
|
||||
// e.g. http://keyman.com.local/keyboards/foo
|
||||
private static let KEYBOARD_MATCH_ROOT_REGEX = try! NSRegularExpression(pattern: "^http(?:s)?://keyman(?:-staging)?\\.com(?:\\.local)?/keyboards([/?].*)?$");
|
||||
private static let KEYBOARD_INSTALL_LINK_REGEX = try! NSRegularExpression(pattern: "^http(?:s)?://keyman(?:-staging)?\\.com(?:\\.localhost)?/keyboards/install/([^?/]+)(?:\\?(.+))?$")
|
||||
// e.g. http://keyman.com.localhost/keyboards/foo
|
||||
private static let KEYBOARD_MATCH_ROOT_REGEX = try! NSRegularExpression(pattern: "^http(?:s)?://keyman(?:-staging)?\\.com(?:\\.localhost)?/keyboards([/?].*)?$");
|
||||
// e.g. https://keyman-staging.com/go/windows/14.0/download-keyboards?version=14.0.146.0
|
||||
private static let KEYBOARD_MATCH_GO_REGEX = try! NSRegularExpression(pattern: "^http(?:s)?://keyman(?:-staging)?\\.com(?:\\.local)?/go/windows/[^/]+/download-keyboards")
|
||||
private static let KEYBOARD_MATCH_GO_REGEX = try! NSRegularExpression(pattern: "^http(?:s)?://keyman(?:-staging)?\\.com(?:\\.localhost)?/go/windows/[^/]+/download-keyboards")
|
||||
|
||||
public static func tryParseKeyboardInstallLink(_ link: URL) -> ParsedKeyboardInstallLink? {
|
||||
let linkString = link.absoluteString
|
||||
|
|
|
|||
|
|
@ -18,14 +18,14 @@ class KeymanHostTests: XCTestCase {
|
|||
}
|
||||
|
||||
/**
|
||||
* Ensures no accidental permanent edits to the .local variant URLs occur.
|
||||
* Ensures no accidental permanent edits to the .localhost variant URLs occur.
|
||||
*/
|
||||
func testLocalSitesUnchanged() {
|
||||
XCTAssertEqual(KeymanHosts.getApiSiteURL(forTier: .stable, useLocal: true),
|
||||
URL.init(string: "http://api.keyman.com.local"))
|
||||
URL.init(string: "http://api.keyman.com.localhost"))
|
||||
XCTAssertEqual(KeymanHosts.getHelpSiteURL(forTier: .stable, useLocal: true),
|
||||
URL.init(string: "http://help.keyman.com.local"))
|
||||
URL.init(string: "http://help.keyman.com.localhost"))
|
||||
XCTAssertEqual(KeymanHosts.getMainSiteURL(forTier: .stable, useLocal: true),
|
||||
URL.init(string: "http://keyman.com.local"))
|
||||
URL.init(string: "http://keyman.com.localhost"))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#!/usr/bin/env bash
|
||||
# Build source packages from nightly builds and upload to PPA
|
||||
|
||||
# shellcheck disable=SC2310 # -e will be disabled in if
|
||||
|
||||
## START STANDARD BUILD SCRIPT INCLUDE
|
||||
# adjust relative paths as necessary
|
||||
THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
||||
|
|
@ -18,6 +20,7 @@ builder_describe \
|
|||
"--upload Upload to launchpad." \
|
||||
"--simulate Simulate the upload to launchpad." \
|
||||
"--no-lintian Don't run lintian while creating source package." \
|
||||
"--no-sign Don't sign the source package." \
|
||||
"--dist=DIST Only upload this distribution. Default: upload all supported dists." \
|
||||
"--packageversion=PACKAGEVERSION String to append to the package version. Default: '1~sil1'." \
|
||||
"--outputdir=OUTPUTDIR Directory for resulting artifacts. Default: \$KEYMAN_ROOT/linux/launchpad." \
|
||||
|
|
@ -39,10 +42,13 @@ else
|
|||
SIM=""
|
||||
fi
|
||||
|
||||
DEBUILD_OPTS=()
|
||||
if builder_has_option --no-lintian; then
|
||||
LINTIAN_OPTS="--no-lintian"
|
||||
else
|
||||
LINTIAN_OPTS=""
|
||||
DEBUILD_OPTS+=("--no-lintian")
|
||||
fi
|
||||
|
||||
if builder_has_option --no-sign; then
|
||||
DEBUILD_OPTS+=("--no-sign")
|
||||
fi
|
||||
|
||||
if [[ "${KEYMAN_TIER}" == "stable" ]]; then
|
||||
|
|
@ -115,8 +121,9 @@ for dist in ${distributions}; do
|
|||
cp "../keyman-changelog" debian/changelog
|
||||
dch -v "${version}-${packageversion}~${dist}" "source package for PPA"
|
||||
dch -D "${dist}" -r ""
|
||||
# shellcheck disable=SC2248 # no quotes for $LINTIAN_OPTS - might be empty string
|
||||
debuild ${LINTIAN_OPTS} -d -S -sa -Zxz
|
||||
# According to the docs, -S is equivalent to --build=source, but that causes debuild to fail.
|
||||
# There is no long option for -sa (passed to dpkg-genchanges)
|
||||
debuild "${DEBUILD_OPTS[@]}" --no-check-builddeps --compression=xz -S -sa
|
||||
done
|
||||
if builder_has_option --upload || builder_has_option --simulate; then
|
||||
cd ..
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
. "${THIS_SCRIPT%/*}/../../resources/build/builder-full.inc.sh"
|
||||
## END STANDARD BUILD SCRIPT INCLUDE
|
||||
|
||||
. "${KEYMAN_ROOT}/resources/locate_emscripten.inc.sh"
|
||||
|
||||
builder_describe \
|
||||
"Verify source tarball and source package" \
|
||||
build \
|
||||
|
|
@ -81,7 +83,7 @@ create_source_package() {
|
|||
cp -r "${KEYMAN_ROOT}/linux/debian" "keyman-${KEYMAN_VERSION}"
|
||||
cp "${target_dir}/keyman_${KEYMAN_VERSION}.pkg.tar.xz" "keyman_${KEYMAN_VERSION}.orig.tar.xz"
|
||||
"keyman-${KEYMAN_VERSION}/linux/scripts/launchpad.sh" --no-download \
|
||||
--dist "$(lsb_release -c -s)" --outputdir "${target_dir}/launchpad" --no-lintian
|
||||
--dist "$(lsb_release -c -s)" --outputdir "${target_dir}/launchpad" --no-lintian --no-sign
|
||||
}
|
||||
|
||||
verify_lintian() {
|
||||
|
|
@ -89,6 +91,18 @@ verify_lintian() {
|
|||
lintian "keyman_${KEYMAN_VERSION}"*source.changes
|
||||
}
|
||||
|
||||
install_emscripten() {
|
||||
local target_dir="$1"
|
||||
if [[ ! -d "${target_dir}/emsdk" ]]; then
|
||||
builder_echo heading "Installing Emscripten for build verification"
|
||||
install_emscripten_into "${target_dir}/emsdk"
|
||||
else
|
||||
builder_echo heading "Emscripten already exists in ${target_dir}/emsdk, skipping installation"
|
||||
fi
|
||||
EMSCRIPTEN_BASE="${target_dir}/emsdk/upstream/emscripten"
|
||||
export EMSCRIPTEN_BASE
|
||||
}
|
||||
|
||||
cd "${KEYMAN_ROOT}/linux"
|
||||
|
||||
if ! builder_has_option --no-create-tarball; then
|
||||
|
|
@ -100,6 +114,9 @@ fi
|
|||
if ! builder_has_option --launchpad-only; then
|
||||
builder_echo start verifySource "Verifying source tarball"
|
||||
extract_source_tarball "${TARGET_DIR}"
|
||||
if builder_is_ci_test_build; then
|
||||
install_emscripten "${TARGET_DIR}"
|
||||
fi
|
||||
verify_can_build "${TARGET_DIR}/keyman"
|
||||
rm -rf "${TARGET_DIR}/keyman"
|
||||
builder_echo end verifySource success "Finished verifying source tarball"
|
||||
|
|
|
|||
|
|
@ -22,15 +22,15 @@
|
|||
|
||||
- (void)windowDidLoad {
|
||||
[super windowDidLoad];
|
||||
|
||||
|
||||
[self.webView setFrameLoadDelegate:(id<WebFrameLoadDelegate>)self];
|
||||
[self.webView setGroupName:@"KMDownloadKB"];
|
||||
[self.webView setPolicyDelegate:(id<WebPolicyDelegate>)self];
|
||||
|
||||
|
||||
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
|
||||
KeymanVersionInfo keymanVersionInfo = [[self AppDelegate] versionInfo];
|
||||
NSString *url = [NSString stringWithFormat:@"https://%@/go/macos/14.0/download-keyboards/?version=%@", keymanVersionInfo.keymanCom, version];
|
||||
|
||||
|
||||
os_log_debug([KMLogs uiLog], "KMDownloadKBWindowController opening url = %@, version = '%@'", url, version);
|
||||
[self.webView.mainFrame loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:url]]];
|
||||
}
|
||||
|
|
@ -45,21 +45,21 @@
|
|||
os_log_debug([KMLogs uiLog], "decidePolicyForNavigationAction, navigating to %@", url);
|
||||
|
||||
// The pattern for matching links matches work in #3602
|
||||
NSString* urlPathMatchKeyboardsInstall = @"^http(?:s)?://keyman(?:-staging)?\\.com(?:\\.local)?/keyboards/install/([^?/]+)(?:\\?(.+))?$";
|
||||
NSString* urlPathMatchKeyboardsInstall = @"^http(?:s)?://keyman(?:-staging)?\\.com(?:\\.localhost)?/keyboards/install/([^?/]+)(?:\\?(.+))?$";
|
||||
// e.g. https://keyman.com/keyboards/install/foo
|
||||
NSString* urlPathMatchKeyboardsRoot = @"^http(?:s)?://keyman(?:-staging)?\\.com(?:\\.local)?/keyboards([/?].*)?$";
|
||||
// http://keyman.com.local/keyboards/foo
|
||||
NSString* urlPathMatchKeyboardsGo = @"^http(?:s)?://keyman(?:-staging)?\\.com(?:\\.local)?/go/macos/[^/]+/download-keyboards";
|
||||
NSString* urlPathMatchKeyboardsRoot = @"^http(?:s)?://keyman(?:-staging)?\\.com(?:\\.localhost)?/keyboards([/?].*)?$";
|
||||
// http://keyman.com.localhost/keyboards/foo
|
||||
NSString* urlPathMatchKeyboardsGo = @"^http(?:s)?://keyman(?:-staging)?\\.com(?:\\.localhost)?/go/macos/[^/]+/download-keyboards";
|
||||
// https://keyman-staging.com/go/macos/14.0/download-keyboards?version=14.0.146.0
|
||||
NSRange range = NSMakeRange(0, url.length);
|
||||
|
||||
|
||||
NSError* error;
|
||||
NSRegularExpression* regexInstall = [NSRegularExpression regularExpressionWithPattern: urlPathMatchKeyboardsInstall options: 0 error: &error];
|
||||
NSRegularExpression* regexRoot = [NSRegularExpression regularExpressionWithPattern: urlPathMatchKeyboardsRoot options: 0 error: &error];
|
||||
NSRegularExpression* regexGo = [NSRegularExpression regularExpressionWithPattern: urlPathMatchKeyboardsGo options: 0 error: &error];
|
||||
|
||||
|
||||
NSArray* matchesInstall = [regexInstall matchesInString:url options:0 range:range];
|
||||
|
||||
|
||||
if(matchesInstall.count > 0) {
|
||||
os_log_debug([KMLogs uiLog], "Delegating download to app delegate.");
|
||||
[listener ignore];
|
||||
|
|
|
|||
24
package-lock.json
generated
24
package-lock.json
generated
|
|
@ -1139,7 +1139,7 @@
|
|||
"restructure": "^3.0.1",
|
||||
"sax": ">=0.6.0",
|
||||
"semver": "^7.5.4",
|
||||
"ws": "^8.17.1",
|
||||
"ws": "^8.20.1",
|
||||
"xmlbuilder": "~11.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -1186,6 +1186,27 @@
|
|||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"developer/src/server/node_modules/ws": {
|
||||
"version": "8.20.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz",
|
||||
"integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@75lb/deep-merge": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@75lb/deep-merge/-/deep-merge-1.1.2.tgz",
|
||||
|
|
@ -13680,6 +13701,7 @@
|
|||
"version": "8.18.1",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz",
|
||||
"integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ THIS_SCRIPT="$(readlink -f "${BASH_SOURCE[0]}")"
|
|||
. "$KEYMAN_ROOT/resources/build/ci/ci-publish.inc.sh"
|
||||
. "$KEYMAN_ROOT/resources/build/ci/npm-packages.inc.sh"
|
||||
. "$KEYMAN_ROOT/resources/build/minimum-versions.inc.sh"
|
||||
. "$KEYMAN_ROOT/resources/locate_emscripten.inc.sh"
|
||||
|
||||
builder_describe \
|
||||
"Publish @keymanapp packages to NPM" \
|
||||
|
|
@ -107,17 +108,10 @@ function install_emscripten() {
|
|||
|
||||
local EMSDK_TEMP
|
||||
EMSDK_TEMP=$(mktemp -d)
|
||||
pushd "${EMSDK_TEMP}"
|
||||
git clone https://github.com/emscripten-core/emsdk.git
|
||||
cd emsdk
|
||||
./emsdk install "${KEYMAN_MIN_VERSION_EMSCRIPTEN}"
|
||||
./emsdk activate "${KEYMAN_MIN_VERSION_EMSCRIPTEN}"
|
||||
cd upstream/emscripten
|
||||
npm install
|
||||
echo "EMSCRIPTEN_BASE=$(pwd)" >> $GITHUB_ENV
|
||||
EMSCRIPTEN_BASE="$(pwd)"
|
||||
install_emscripten_into "${EMSDK_TEMP}"
|
||||
EMSCRIPTEN_BASE="${EMSDK_TEMP}/upstream/emscripten"
|
||||
export EMSCRIPTEN_BASE
|
||||
popd
|
||||
echo "EMSCRIPTEN_BASE=${EMSCRIPTEN_BASE}" >> "${GITHUB_ENV}"
|
||||
}
|
||||
|
||||
function install_meson() {
|
||||
|
|
|
|||
|
|
@ -124,7 +124,12 @@ function add_zip_files() {
|
|||
if [[ -z "${SEVENZ+x}" ]]; then
|
||||
if builder_is_windows; then
|
||||
if [[ -z "${SEVENZ_HOME+x}" ]]; then
|
||||
SEVENZ="$(command -v 7z.exe)"
|
||||
SEVENZ="$(command -v 7z.exe || true)"
|
||||
if [[ -z "${SEVENZ}" ]]; then
|
||||
builder_die "7z.exe not found on path. Please install 7-Zip " \
|
||||
"and ensure 7z.exe is on the path or set SEVENZ_HOME " \
|
||||
"environment variable to the folder containing 7z.exe."
|
||||
fi
|
||||
else
|
||||
SEVENZ="${SEVENZ_HOME}/7z.exe"
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ locate_emscripten() {
|
|||
if [[ -z "${EMSCRIPTEN_BASE:-}" ]]; then
|
||||
if [[ -z "${EMCC:-}" ]]; then
|
||||
local EMCC
|
||||
EMCC="$(command -v "${EMCC_EXECUTABLE}")"
|
||||
EMCC="$(command -v "${EMCC_EXECUTABLE}" || true)"
|
||||
[[ -z "${EMCC}" ]] && builder_die "locate_emscripten: Could not locate emscripten (${EMCC_EXECUTABLE}) on the path or with \$EMCC or \$EMSCRIPTEN_BASE"
|
||||
fi
|
||||
[[ -f "${EMCC}" && ! -x "${EMCC}" ]] && builder_die "locate_emscripten: Variable EMCC (${EMCC}) points to ${EMCC_EXECUTABLE} but it is not executable"
|
||||
|
|
@ -117,3 +117,22 @@ _select_emscripten_version_with_emsdk() {
|
|||
fi
|
||||
)
|
||||
}
|
||||
|
||||
install_emscripten_into() {
|
||||
if [[ -z "${1:-}" ]]; then
|
||||
builder_die "${FUNCNAME[0]} requires a directory argument"
|
||||
fi
|
||||
|
||||
local EMSDK_DIR=$1
|
||||
builder_heading "Installing emscripten into ${EMSDK_DIR}"
|
||||
|
||||
mkdir -p "${EMSDK_DIR}"
|
||||
(
|
||||
cd "${EMSDK_DIR}"
|
||||
git clone https://github.com/emscripten-core/emsdk.git .
|
||||
./emsdk install "${KEYMAN_MIN_VERSION_EMSCRIPTEN}"
|
||||
./emsdk activate "${KEYMAN_MIN_VERSION_EMSCRIPTEN}"
|
||||
cd upstream/emscripten
|
||||
npm install
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue