From 849cda09bb3d01ce232369d702455dbe7d6820c6 Mon Sep 17 00:00:00 2001 From: jahorton Date: Tue, 5 May 2020 14:47:32 +0700 Subject: [PATCH 1/7] feat(web): starts base-level sentry linkage/testing setup --- .../core/web/tools/sentry-manager/.gitignore | 18 +++ .../tools/sentry-manager/package-lock.json | 76 +++++++++++++ .../web/tools/sentry-manager/package.json | 25 +++++ .../web/tools/sentry-manager/src/build.sh | 63 +++++++++++ .../web/tools/sentry-manager/src/index.ts | 69 ++++++++++++ .../tools/sentry-manager/src/tsconfig.json | 15 +++ lerna.json | 1 + resources/build/build-utils.sh | 1 + web/package-lock.json | 88 ++++++++++++++- web/package.json | 5 +- web/testing/index.html | 3 +- web/testing/sentry-integration/index.html | 103 ++++++++++++++++++ 12 files changed, 459 insertions(+), 8 deletions(-) create mode 100644 common/core/web/tools/sentry-manager/.gitignore create mode 100644 common/core/web/tools/sentry-manager/package-lock.json create mode 100644 common/core/web/tools/sentry-manager/package.json create mode 100755 common/core/web/tools/sentry-manager/src/build.sh create mode 100644 common/core/web/tools/sentry-manager/src/index.ts create mode 100644 common/core/web/tools/sentry-manager/src/tsconfig.json create mode 100644 web/testing/sentry-integration/index.html diff --git a/common/core/web/tools/sentry-manager/.gitignore b/common/core/web/tools/sentry-manager/.gitignore new file mode 100644 index 0000000000..a9e0c25514 --- /dev/null +++ b/common/core/web/tools/sentry-manager/.gitignore @@ -0,0 +1,18 @@ +# Build products +src/environment.inc.ts + +# Legacy build folders. +output/ +build/ +embedded/ + +# Current build output folder (matches standard node publish location). +dist/ + +# Other local files. +node_modules/ +unit_tests/modernizr.js +source/environment.inc.ts +**/.idea/**/*.xml +**/*.iml +**/*.kpj.user \ No newline at end of file diff --git a/common/core/web/tools/sentry-manager/package-lock.json b/common/core/web/tools/sentry-manager/package-lock.json new file mode 100644 index 0000000000..ed79570cc0 --- /dev/null +++ b/common/core/web/tools/sentry-manager/package-lock.json @@ -0,0 +1,76 @@ +{ + "name": "@keymanapp/sentry-manager", + "version": "14.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@sentry/browser": { + "version": "5.15.5", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.15.5.tgz", + "integrity": "sha512-rqDvjk/EvogfdbZ4TiEpxM/lwpPKmq23z9YKEO4q81+1SwJNua53H60dOk9HpRU8nOJ1g84TMKT2Ov8H7sqDWA==", + "requires": { + "@sentry/core": "5.15.5", + "@sentry/types": "5.15.5", + "@sentry/utils": "5.15.5", + "tslib": "^1.9.3" + } + }, + "@sentry/core": { + "version": "5.15.5", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.15.5.tgz", + "integrity": "sha512-enxBLv5eibBMqcWyr+vApqeix8uqkfn0iGsD3piKvoMXCgKsrfMwlb/qo9Ox0lKr71qIlZVt+9/A2vZohdgnlg==", + "requires": { + "@sentry/hub": "5.15.5", + "@sentry/minimal": "5.15.5", + "@sentry/types": "5.15.5", + "@sentry/utils": "5.15.5", + "tslib": "^1.9.3" + } + }, + "@sentry/hub": { + "version": "5.15.5", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.15.5.tgz", + "integrity": "sha512-zX9o49PcNIVMA4BZHe//GkbQ4Jx+nVofqU/Il32/IbwKhcpPlhGX3c1sOVQo4uag3cqd/JuQsk+DML9TKkN0Lw==", + "requires": { + "@sentry/types": "5.15.5", + "@sentry/utils": "5.15.5", + "tslib": "^1.9.3" + } + }, + "@sentry/minimal": { + "version": "5.15.5", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.15.5.tgz", + "integrity": "sha512-zQkkJ1l9AjmU/Us5IrOTzu7bic4sTPKCatptXvLSTfyKW7N6K9MPIIFeSpZf9o1yM2sRYdK7GV08wS2eCT3JYw==", + "requires": { + "@sentry/hub": "5.15.5", + "@sentry/types": "5.15.5", + "tslib": "^1.9.3" + } + }, + "@sentry/types": { + "version": "5.15.5", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.15.5.tgz", + "integrity": "sha512-F9A5W7ucgQLJUG4LXw1ZIy4iLevrYZzbeZ7GJ09aMlmXH9PqGThm1t5LSZlVpZvUfQ2rYA8NU6BdKJSt7B5LPw==" + }, + "@sentry/utils": { + "version": "5.15.5", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.15.5.tgz", + "integrity": "sha512-Nl9gl/MGnzSkuKeo3QaefoD/OJrFLB8HmwQ7HUbTXb6E7yyEzNKAQMHXGkwNAjbdYyYbd42iABP6Y5F/h39NtA==", + "requires": { + "@sentry/types": "5.15.5", + "tslib": "^1.9.3" + } + }, + "tslib": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz", + "integrity": "sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==" + }, + "typescript": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz", + "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==", + "dev": true + } + } +} diff --git a/common/core/web/tools/sentry-manager/package.json b/common/core/web/tools/sentry-manager/package.json new file mode 100644 index 0000000000..bb0a0099bf --- /dev/null +++ b/common/core/web/tools/sentry-manager/package.json @@ -0,0 +1,25 @@ +{ + "name": "@keymanapp/sentry-manager", + "version": "14.0.0", + "description": "Common setup for KeymanWeb's Sentry-based error reporting", + "main": "dist/index.js", + "scripts": { + "tsc": "tsc" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/keymanapp/keyman.git" + }, + "author": "SIL International", + "license": "MIT", + "bugs": { + "url": "https://github.com/keymanapp/keyman/issues" + }, + "homepage": "https://github.com/keymanapp/keyman#readme", + "devDependencies": { + "typescript": "^3.8.3" + }, + "dependencies": { + "@sentry/browser": "^5.15.5" + } +} diff --git a/common/core/web/tools/sentry-manager/src/build.sh b/common/core/web/tools/sentry-manager/src/build.sh new file mode 100755 index 0000000000..f35ed91ed7 --- /dev/null +++ b/common/core/web/tools/sentry-manager/src/build.sh @@ -0,0 +1,63 @@ +#! /bin/bash +# +# Compiles development-related KeymanWeb resources for use with developing/running tests. +# - the Recorder module (for engine tests) +# - the DOM module (for touch-alias and element-interface tests) + +## START STANDARD BUILD SCRIPT INCLUDE +# adjust relative paths as necessary +THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")" +SCRIPT_DIR="$(dirname "$THIS_SCRIPT")" +KEYMAN_ROOT="$(dirname "$THIS_SCRIPT")/../../../../../.." +. "$KEYMAN_ROOT/resources/build/build-utils.sh" +. "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" +## END STANDARD BUILD SCRIPT INCLUDE + +display_usage ( ) { + echo "build.sh [-skip-dependency-install]" + echo + echo " -skip-dependency-install skips the `lerna bootstrap` dependency check." + echo " (or -S) Intended for use when this script is called by another build script." + echo "" + echo " If more than one target is specified, the last one will take precedence." + exit 1 +} + +# Establish default build parameters +set_default_vars ( ) { + FETCH_DEPS=true +} + +set_default_vars + +# Parse args +while [[ $# -gt 0 ]] ; do + key="$1" + case $key in + -skip-package-install|-S) + set_default_vars + FETCH_DEPS=false + ;; + esac + shift # past argument +done + +if [ $FETCH_DEPS = true ]; then + verify_npm_setup +fi + +# Definition of global compile constants +OUTPUT_DIR="dist" +OUTPUT="index.js" + +# Ensures that we rely first upon the local npm-based install of Typescript. +# (Facilitates automated setup for build agents.) +PATH="../node_modules/.bin:$PATH" + +compiler="npm run tsc --" +compilecmd="$compiler" + +$compilecmd -p "$SCRIPT_DIR/tsconfig.json" +if [ $? -ne 0 ]; then + fail "KeymanWeb recorder-core compilation failed." +fi \ No newline at end of file diff --git a/common/core/web/tools/sentry-manager/src/index.ts b/common/core/web/tools/sentry-manager/src/index.ts new file mode 100644 index 0000000000..551171d2f4 --- /dev/null +++ b/common/core/web/tools/sentry-manager/src/index.ts @@ -0,0 +1,69 @@ +/// + +declare var com; + +(function() { + var ALIASABLE_FILES = { + 'keymanweb.js': 'keymanweb.js', + 'kmwuibutton.js': 'kmwuibutton.js', + 'kmwuifloat.js': 'kmwuifloat.js', + 'kmwuitoggle.js': 'kmwuitoggle.js', + 'kmwuitoolbar.js': 'kmwuitoolbar.js' + // Also add entries for the naming system used by Android and iOS - and map them to the EMBEDDED upload, not the std 'native' one. + } + + // If we've recognized one of our source files, + function aliasFilename(filename: string) { + if(!ALIASABLE_FILES[filename]) { + return null; + } + + filename = ALIASABLE_FILES[filename]; + + switch(location.protocol) { + case 'http:': + return 'http://' + location.host + '/' + filename; + case 'file:': + return 'file:///' + filename; + default: + return null; + } + } + + // Filters all expected but unnecessary path prefixes, affixes, and suffixes reported by Sentry from our products. + // This allows us to mask all different sorts of installations with a single uploaded path. + function pathFilter(event: any) { + // Get the underlying JS error. + let exception = event.exception; + + // Iterate through all wrapped exceptions. + for(let e of exception.values) { + for(let frame of e.stacktrace.frames) { + let URL = frame.filename as string; + let filename: string = ''; + try { + filename = URL.substr(URL.lastIndexOf('/')+1); + if(ALIASABLE_FILES[filename]) { + // Make the file look like it's at the host's root so that the Sentry server will recognize it + // regardless of actual filepath position. + frame.filename = aliasFilename(filename) || frame.filename; + } + } catch { + // not sure what to do yet, but... don't make more errors in the error handler. That's a bad idea. + } + } + } + + return event; + } + + // Do the actual Sentry initialization. + //@ts-ignore + Sentry.init({ + beforeSend: pathFilter, + // FIXME: DO NOT LEAVE IN PRODUCTION! + debug: true, + dsn: 'https://cf96f32d107c4286ab2fd82af49c4d3b@sentry.keyman.com/11', // keyman-web DSN + release: com.keyman.environment.SENTRY_RELEASE + }); +})() \ No newline at end of file diff --git a/common/core/web/tools/sentry-manager/src/tsconfig.json b/common/core/web/tools/sentry-manager/src/tsconfig.json new file mode 100644 index 0000000000..73ea8a97ad --- /dev/null +++ b/common/core/web/tools/sentry-manager/src/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "allowJs": true, + "module": "none", + "outDir": "../dist/", + "inlineSources": true, + "sourceMap": true, + "target": "es5", + "lib": ["es6", "dom"], + "outFile": "../dist/index.js" + }, + "files": [ + "index.ts" + ] +} \ No newline at end of file diff --git a/lerna.json b/lerna.json index 26e6afa496..a63941674c 100644 --- a/lerna.json +++ b/lerna.json @@ -3,6 +3,7 @@ "common/core/web/input-processor", "common/core/web/keyboard-processor", "common/core/web/tools/recorder", + "common/core/web/tools/sentry-manager", "common/lexical-model-types", "common/predictive-text", "web" diff --git a/resources/build/build-utils.sh b/resources/build/build-utils.sh index bff6e82974..351c29d10f 100755 --- a/resources/build/build-utils.sh +++ b/resources/build/build-utils.sh @@ -195,6 +195,7 @@ exportEnvironmentDefinitionTS ( ) { namespace com.keyman.environment { export var VERSION = \"$VERSION_RELEASE\"; export var BUILD = $VERSION_PATCH; + export var SENTRY_RELEASE = \"release-$VERSION_WITH_TAG\"; } " >> $ENVIRONMENT_TS } \ No newline at end of file diff --git a/web/package-lock.json b/web/package-lock.json index 4e0464a053..c0e2daf090 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -4,6 +4,70 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@sentry/cli": { + "version": "1.52.3", + "resolved": "https://registry.npmjs.org/@sentry/cli/-/cli-1.52.3.tgz", + "integrity": "sha512-QOSIg5hxAEa6v6H7oEeF6A/Rpa0wloMhbu0Qed6zHv3lyoqf0Z34Kq2jCXdqGsOE3IzkO+3CNy81F6361j5TKg==", + "dev": true, + "requires": { + "https-proxy-agent": "^5.0.0", + "mkdirp": "^0.5.5", + "node-fetch": "^2.6.0", + "progress": "^2.0.3", + "proxy-from-env": "^1.1.0" + }, + "dependencies": { + "agent-base": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.0.tgz", + "integrity": "sha512-j1Q7cSCqN+AwrmDd+pzgqc0/NpC655x2bUf5ZjRIO77DcNBFmh+OgRNzF6OKdCC9RSCb19fGd99+bhXFdkRNqw==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "https-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", + "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, "@types/node": { "version": "11.9.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-11.9.4.tgz", @@ -1552,12 +1616,6 @@ } } }, - "mocha-teamcity-reporter": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mocha-teamcity-reporter/-/mocha-teamcity-reporter-3.0.0.tgz", - "integrity": "sha512-FyGgmtFfW2nDwEZU3mrjQShAAK/zhGivwY4HCsqoDoyeS8vV8HGdq1Dn2P+SFaIoCeXTQ0Z+5xVRyikYaKrW5w==", - "dev": true - }, "modernizr": { "version": "3.7.1", "resolved": "https://registry.npmjs.org/modernizr/-/modernizr-3.7.1.tgz", @@ -1585,6 +1643,12 @@ "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", "dev": true }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==", + "dev": true + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1736,6 +1800,18 @@ "integrity": "sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA==", "dev": true }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, "ps-tree": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ps-tree/-/ps-tree-1.1.1.tgz", diff --git a/web/package.json b/web/package.json index af522da803..b21b5bb91a 100644 --- a/web/package.json +++ b/web/package.json @@ -18,6 +18,8 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { + "@keymanapp/sentry-manager": "^14.0.0", + "@sentry/cli": "^1.52.3", "chai": "^4.2.0", "google-closure-compiler-java": "^20200224.0.0", "html2canvas": "^1.0.0-alpha.12", @@ -45,7 +47,8 @@ "karma": "karma", "lerna": "cd ../ && npm run lerna --", "mocha": "mocha", - "modernizr": "modernizr" + "modernizr": "modernizr", + "sentry-cli": "sentry-cli" }, "dependencies": { "@keymanapp/input-processor": "^14.0.72", diff --git a/web/testing/index.html b/web/testing/index.html index 5084af2234..5a3a76b5ec 100644 --- a/web/testing/index.html +++ b/web/testing/index.html @@ -19,7 +19,7 @@ -

KeymanWeb 12 Testing

+

KeymanWeb 14 Testing

Test unminified Keymanweb

Test unminified Keymanweb in manual-attachment mode.

Tests keyboard error-handling functionality

@@ -30,6 +30,7 @@

Prediction UI testing

Toolbar UI testing

Tests keyboard documentation rendering.

+

Tests Sentry error-reporting functionality

Auto-attachment mode tests

Test desktop MutationObserver functionality

Light test for touch-based MutationObserver functionality

diff --git a/web/testing/sentry-integration/index.html b/web/testing/sentry-integration/index.html new file mode 100644 index 0000000000..ab7b8323b3 --- /dev/null +++ b/web/testing/sentry-integration/index.html @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + KeymanWeb Sample Page - Unminified Source + + + + + + + + + + + + + + + + + + + + + + +

KeymanWeb Sample Page - Unminified Source

+ +
+ +
+ +

Type in your language in this text area:

+ + +

or in this input field:

+ + + +

Add a keyboard by keyboard name:

+ + + +

Add a keyboard by BCP-47 language code:

+ + + +

Add a keyboard by language name:

+ + + +

Return to testing home page

+
+ + + + + From 49488ed17f9dc78c44fda49c3ca47e8cd048ffda Mon Sep 17 00:00:00 2001 From: jahorton Date: Thu, 7 May 2020 12:36:08 +0700 Subject: [PATCH 2/7] feat(web): adds some event metadata for reports --- .../web/tools/sentry-manager/src/index.ts | 23 ++++++++++++++++++- web/source/kmwbase.ts | 18 +++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/common/core/web/tools/sentry-manager/src/index.ts b/common/core/web/tools/sentry-manager/src/index.ts index 551171d2f4..8493d79850 100644 --- a/common/core/web/tools/sentry-manager/src/index.ts +++ b/common/core/web/tools/sentry-manager/src/index.ts @@ -57,10 +57,31 @@ declare var com; return event; } + function attachEventMetadata(event: any) { + event.extra = event.extra || []; + event.extra.push({ + initialized: window['keyman']['getDebugInfo']() + }); + + return event; + } + + /** + * Pre-processes a Sentry event object to provide more metadata and enhance the Sentry server's + * ability to match the error against release artifacts. + * @param event A Sentry-generated event + */ + function eventPreparer(event: any) { + event = pathFilter(event); + event = attachEventMetadata(event); + + return null; //event + } + // Do the actual Sentry initialization. //@ts-ignore Sentry.init({ - beforeSend: pathFilter, + beforeSend: eventPreparer, // FIXME: DO NOT LEAVE IN PRODUCTION! debug: true, dsn: 'https://cf96f32d107c4286ab2fd82af49c4d3b@sentry.keyman.com/11', // keyman-web DSN diff --git a/web/source/kmwbase.ts b/web/source/kmwbase.ts index 0ca1f26083..431b701370 100644 --- a/web/source/kmwbase.ts +++ b/web/source/kmwbase.ts @@ -177,6 +177,24 @@ namespace com.keyman { dom.DOMEventHandlers.states = new dom.CommonDOMStates(); } + /** + * Returns a generalized metadata object about the state of KMW for use with error reporting. + */ + ['getDebugInfo']() { + let metadata = { + attachType: this.options.attachType, + initialized: this.initialized, + isEmbedded: this.isEmbedded, + ui: this.ui ? this.ui.name : null + } + + if(this.util.device.touchable) { + metadata.ui = 'touch'; + } + + return metadata; + } + /** * Expose font testing to allow checking that SpecialOSK or custom font has * been correctly loaded by browser From 660d8bf3cd36fed4277d4ce315082e7b504e5a02 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 13 May 2020 10:05:16 +0700 Subject: [PATCH 3/7] feat(web): better debug setup, includes build during KMW build --- .../core/web/tools/sentry-manager/.gitignore | 1 - .../web/tools/sentry-manager/src/build.sh | 3 + .../web/tools/sentry-manager/src/index.ts | 64 +++++++++++++------ .../tools/sentry-manager/src/tsconfig.json | 1 + web/source/build.sh | 9 +++ web/testing/sentry-integration/index.html | 5 +- 6 files changed, 63 insertions(+), 20 deletions(-) diff --git a/common/core/web/tools/sentry-manager/.gitignore b/common/core/web/tools/sentry-manager/.gitignore index a9e0c25514..a5025863e3 100644 --- a/common/core/web/tools/sentry-manager/.gitignore +++ b/common/core/web/tools/sentry-manager/.gitignore @@ -12,7 +12,6 @@ dist/ # Other local files. node_modules/ unit_tests/modernizr.js -source/environment.inc.ts **/.idea/**/*.xml **/*.iml **/*.kpj.user \ No newline at end of file diff --git a/common/core/web/tools/sentry-manager/src/build.sh b/common/core/web/tools/sentry-manager/src/build.sh index f35ed91ed7..7760f8c873 100755 --- a/common/core/web/tools/sentry-manager/src/build.sh +++ b/common/core/web/tools/sentry-manager/src/build.sh @@ -54,6 +54,9 @@ OUTPUT="index.js" # (Facilitates automated setup for build agents.) PATH="../node_modules/.bin:$PATH" +# Ensure the environment definition variables are available. +exportEnvironmentDefinitionTS + compiler="npm run tsc --" compilecmd="$compiler" diff --git a/common/core/web/tools/sentry-manager/src/index.ts b/common/core/web/tools/sentry-manager/src/index.ts index 8493d79850..44c877a0ac 100644 --- a/common/core/web/tools/sentry-manager/src/index.ts +++ b/common/core/web/tools/sentry-manager/src/index.ts @@ -1,8 +1,12 @@ /// -declare var com; +namespace com.keyman { + /** + * Controls whether or not the generated Sentry event is logged to the console (true) + * or sent to our Sentry server (false). + */ + let DEBUG = true; -(function() { var ALIASABLE_FILES = { 'keymanweb.js': 'keymanweb.js', 'kmwuibutton.js': 'kmwuibutton.js', @@ -58,11 +62,7 @@ declare var com; } function attachEventMetadata(event: any) { - event.extra = event.extra || []; - event.extra.push({ - initialized: window['keyman']['getDebugInfo']() - }); - + event.extra.keymanState = window['keyman']['getDebugInfo'](); return event; } @@ -75,16 +75,44 @@ declare var com; event = pathFilter(event); event = attachEventMetadata(event); - return null; //event + if(DEBUG) { + console.log("DEBUG: event object for Sentry") + console.log(event); + return null; //event + } else { + return event; + } } - // Do the actual Sentry initialization. - //@ts-ignore - Sentry.init({ - beforeSend: eventPreparer, - // FIXME: DO NOT LEAVE IN PRODUCTION! - debug: true, - dsn: 'https://cf96f32d107c4286ab2fd82af49c4d3b@sentry.keyman.com/11', // keyman-web DSN - release: com.keyman.environment.SENTRY_RELEASE - }); -})() \ No newline at end of file + /** + * Allows debugging our custom event preparation code without bombarding Sentry with errors + * during development. + * @param event + */ + function __metaPreparer(event: any) { + if(DEBUG) { + try { + return eventPreparer(event); + } catch(err) { + console.log(err); + } + } else { + // If not in DEBUG mode, simply forward to the actual preparer; we should be notified + // of any event-prep errors that may occur. + return eventPreparer(event); + } + } + + export class KeymanSentryManager { + init() { + // Do the actual Sentry initialization. + //@ts-ignore + Sentry.init({ + beforeSend: __metaPreparer, + debug: DEBUG, + dsn: 'https://cf96f32d107c4286ab2fd82af49c4d3b@sentry.keyman.com/11', // keyman-web DSN + release: com.keyman.environment.SENTRY_RELEASE + }); + } + } +} \ No newline at end of file diff --git a/common/core/web/tools/sentry-manager/src/tsconfig.json b/common/core/web/tools/sentry-manager/src/tsconfig.json index 73ea8a97ad..b23d357baa 100644 --- a/common/core/web/tools/sentry-manager/src/tsconfig.json +++ b/common/core/web/tools/sentry-manager/src/tsconfig.json @@ -10,6 +10,7 @@ "outFile": "../dist/index.js" }, "files": [ + "environment.inc.ts", "index.ts" ] } \ No newline at end of file diff --git a/web/source/build.sh b/web/source/build.sh index 1c41984a41..e536b0a9f0 100755 --- a/web/source/build.sh +++ b/web/source/build.sh @@ -314,6 +314,7 @@ if [ $DO_MINIFY = true ]; then exit 1 fi + # Only run if BOTH cases are true b/c it's a minification-focused 'dependency'. if [ $FETCH_DEPS = true ]; then # Also, build our sourcemap-root tool for cleaning up the minified version's sourcemaps. echo "Compiling build tools for minified build products" @@ -324,6 +325,14 @@ fi if [ $BUILD_CORE = true ]; then CORE_FLAGS="-skip-package-install" + + # Build the sentry-manager module - it's used in embedded contexts and on one testing page. + echo "${TERM_HEADING}Compiling KeymanWeb's sentry-manager module...${NORMAL}" + pushd ../../common/core/web/tools/sentry-manager/src + ./build.sh $CORE_FLAGS || fail "Failed to compile the sentry-manager module" + popd + echo "${TERM_HEADING}sentry-manager module compiled successfully.${NORMAL}" + if [ $BUILD_LMLAYER = false ]; then CORE_FLAGS="$CORE_FLAGS -test" fi diff --git a/web/testing/sentry-integration/index.html b/web/testing/sentry-integration/index.html index ab7b8323b3..7778ab4766 100644 --- a/web/testing/sentry-integration/index.html +++ b/web/testing/sentry-integration/index.html @@ -42,6 +42,9 @@ kmw.init({ attachType:'auto' }); + + var sentryManager = new com.keyman.KeymanSentryManager(); + sentryManager.init(); @@ -51,7 +54,7 @@ -

KeymanWeb Sample Page - Unminified Source

+

KeymanWeb Sample Page - Sentry Setup test host

- + diff --git a/web/testing/sentry-integration/timeout.js b/web/testing/sentry-integration/timeout.js new file mode 100644 index 0000000000..2af6bf907a --- /dev/null +++ b/web/testing/sentry-integration/timeout.js @@ -0,0 +1,10 @@ +(function() { + var me = document.currentScript; + console.log(me); + document.body.addEventListener('load', function(e) { + // Prevent the element's onload event from firing + if(e.srcElement == me || e.target == me) { + e.cancelBubble = true; + } + }, {capture: true}); +})(); \ No newline at end of file diff --git a/web/testing/sentry-integration/unparsable.js b/web/testing/sentry-integration/unparsable.js new file mode 100644 index 0000000000..76dd4f3a1d --- /dev/null +++ b/web/testing/sentry-integration/unparsable.js @@ -0,0 +1 @@ +// As there is no parsable keyboard here, we need to report that we couldn't register the requested keyboard. \ No newline at end of file From 2eec927affc5f740045ba9f98d5e517fc74eb6c4 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 13 May 2020 13:26:25 +0700 Subject: [PATCH 5/7] feat(web): builds now upload sentry artifacts --- web/source/build.sh | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/web/source/build.sh b/web/source/build.sh index e536b0a9f0..eb3e485f7e 100755 --- a/web/source/build.sh +++ b/web/source/build.sh @@ -206,6 +206,13 @@ INTERMEDIATE="../intermediate" SOURCE="." NODE_SOURCE="source" +SENTRY_RELEASE_VERSION="release-$VERSION_WITH_TAG" + +export SENTRY_URL="https://sentry.keyman.com" +export SENTRY_ORG=keyman +export SENTRY_PROJECT=keyman-web +export SENTRY_LOG_LEVEL=info + readonly WEB_OUTPUT readonly EMBED_OUTPUT readonly SOURCE @@ -227,6 +234,7 @@ set_default_vars ( ) { BUILD_DEBUG_EMBED=false BUILD_COREWEB=true DO_MINIFY=true + DO_NATIVE_SENTRY=true FETCH_DEPS=true } @@ -264,6 +272,7 @@ while [[ $# -gt 0 ]] ; do BUILD_FULLWEB=false BUILD_UI=false BUILD_COREWEB=false + DO_NATIVE_SENTRY=false ;; -web) set_default_vars @@ -276,12 +285,14 @@ while [[ $# -gt 0 ]] ; do BUILD_COREWEB=false BUILD_FULLWEB=false BUILD_DEBUG_EMBED=true + DO_NATIVE_SENTRY=false ;; -h|-?) display_usage ;; -no_minify) DO_MINIFY=false + DO_NATIVE_SENTRY=false ;; -clean) clean @@ -390,6 +401,13 @@ if [ $BUILD_EMBED = true ]; then echo echo KMEA/KMEI version $VERSION compiled and saved under $EMBED_OUTPUT echo + + # We always publish "embedded" mode to Sentry when minifying. + pushd $EMBED_OUTPUT + echo "Uploading to Sentry..." + npm run sentry-cli -- releases files "$SENTRY_RELEASE_VERSION" upload-sourcemaps --strip-common-prefix release/embedded --rewrite --ext js --ext map --ext ts || fail "Sentry upload failed." + echo "Upload successful." + popd fi fi @@ -471,6 +489,16 @@ if [ $BUILD_UI = true ]; then fi fi +# Has its own flag due to complex logic. Should run if $DO_MINIFY && ($BUILD_COREWEB || $BUILD_UI) +# If only one of the two (COREWEB, UI) is run, we assume the un-run portion is 'current' and still upload it. +if [ $DO_NATIVE_SENTRY = true ]; then + pushd $WEB_OUTPUT + echo "Uploading to Sentry..." + npm run sentry-cli -- releases files "$SENTRY_RELEASE_VERSION" upload-sourcemaps --strip-common-prefix release/web/ --rewrite --ext js --ext map --ext ts || fail "Sentry upload failed." + echo "Upload successful." + popd +fi + if [ $BUILD_DEBUG_EMBED = true ]; then # We currently have an issue with sourcemaps for minified versions. # We should use the unminified one instead for now. @@ -479,4 +507,3 @@ if [ $BUILD_DEBUG_EMBED = true ]; then cp $EMBED_OUTPUT_NO_MINI/keyman.js.map $EMBED_OUTPUT/keyman.js.map echo Uncompiled embedded application saved as keyman.js fi - From b57309846b88a2c1ad2ecaa638b54356655209c6 Mon Sep 17 00:00:00 2001 From: jahorton Date: Wed, 13 May 2020 13:45:56 +0700 Subject: [PATCH 6/7] feat(web): reports detected device --- web/source/kmwbase.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/web/source/kmwbase.ts b/web/source/kmwbase.ts index 431b701370..418078c5d2 100644 --- a/web/source/kmwbase.ts +++ b/web/source/kmwbase.ts @@ -183,6 +183,7 @@ namespace com.keyman { ['getDebugInfo']() { let metadata = { attachType: this.options.attachType, + device: this.util.device, initialized: this.initialized, isEmbedded: this.isEmbedded, ui: this.ui ? this.ui.name : null From 0eec1dd5fd41da0a20eefcbe51a7e7b56080733d Mon Sep 17 00:00:00 2001 From: jahorton Date: Fri, 15 May 2020 15:50:36 +0700 Subject: [PATCH 7/7] change(web): addresses PR review comments (#3122) --- .../tools/sentry-manager/package-lock.json | 2 +- .../web/tools/sentry-manager/package.json | 2 +- .../web/tools/sentry-manager/src/build.sh | 8 ++-- .../web/tools/sentry-manager/src/index.ts | 38 ++++++++++++------- web/package.json | 2 +- web/source/keyboards/kmwkeyboards.ts | 6 ++- web/testing/sentry-integration/index.html | 2 +- web/tools/recorder/build.sh | 3 +- 8 files changed, 39 insertions(+), 24 deletions(-) diff --git a/common/core/web/tools/sentry-manager/package-lock.json b/common/core/web/tools/sentry-manager/package-lock.json index ed79570cc0..daa0f31829 100644 --- a/common/core/web/tools/sentry-manager/package-lock.json +++ b/common/core/web/tools/sentry-manager/package-lock.json @@ -1,5 +1,5 @@ { - "name": "@keymanapp/sentry-manager", + "name": "@keymanapp/web-sentry-manager", "version": "14.0.0", "lockfileVersion": 1, "requires": true, diff --git a/common/core/web/tools/sentry-manager/package.json b/common/core/web/tools/sentry-manager/package.json index bb0a0099bf..68a4379cd5 100644 --- a/common/core/web/tools/sentry-manager/package.json +++ b/common/core/web/tools/sentry-manager/package.json @@ -1,5 +1,5 @@ { - "name": "@keymanapp/sentry-manager", + "name": "@keymanapp/web-sentry-manager", "version": "14.0.0", "description": "Common setup for KeymanWeb's Sentry-based error reporting", "main": "dist/index.js", diff --git a/common/core/web/tools/sentry-manager/src/build.sh b/common/core/web/tools/sentry-manager/src/build.sh index 7760f8c873..b1adcfa1ab 100755 --- a/common/core/web/tools/sentry-manager/src/build.sh +++ b/common/core/web/tools/sentry-manager/src/build.sh @@ -7,12 +7,12 @@ ## START STANDARD BUILD SCRIPT INCLUDE # adjust relative paths as necessary THIS_SCRIPT="$(greadlink -f "${BASH_SOURCE[0]}" 2>/dev/null || readlink -f "${BASH_SOURCE[0]}")" -SCRIPT_DIR="$(dirname "$THIS_SCRIPT")" -KEYMAN_ROOT="$(dirname "$THIS_SCRIPT")/../../../../../.." -. "$KEYMAN_ROOT/resources/build/build-utils.sh" +. "$(dirname "$THIS_SCRIPT")/../../../../../../resources/build/build-utils.sh" . "$KEYMAN_ROOT/resources/shellHelperFunctions.sh" ## END STANDARD BUILD SCRIPT INCLUDE +SCRIPT_DIR="$(dirname "$THIS_SCRIPT")" + display_usage ( ) { echo "build.sh [-skip-dependency-install]" echo @@ -62,5 +62,5 @@ compilecmd="$compiler" $compilecmd -p "$SCRIPT_DIR/tsconfig.json" if [ $? -ne 0 ]; then - fail "KeymanWeb recorder-core compilation failed." + fail "Compilation of package for Sentry integration with KeymanWeb failed." fi \ No newline at end of file diff --git a/common/core/web/tools/sentry-manager/src/index.ts b/common/core/web/tools/sentry-manager/src/index.ts index 1c8e0b8b3c..988757031a 100644 --- a/common/core/web/tools/sentry-manager/src/index.ts +++ b/common/core/web/tools/sentry-manager/src/index.ts @@ -53,6 +53,7 @@ namespace com.keyman { // Filters all expected but unnecessary path prefixes, affixes, and suffixes reported by Sentry from our products. // This allows us to mask all different sorts of installations with a single uploaded path. + // Modifies original object. pathFilter(event: any) { // Get the underlying JS error. let exception = event.exception; @@ -74,52 +75,61 @@ namespace com.keyman { } } } - - return event; } + // Attaches some useful debugging information to the specified object, pass-by-reference style. attachEventMetadata(event: any) { // Ensure that the 'extra' object exists. (May not exist for synthetic/custom Errors.) event.extra = event.extra || {}; event.extra.keymanState = window['keyman']['getDebugInfo'](); event.extra.keymanHostPlatform = this.keymanPlatform; - return event; } /** - * Pre-processes a Sentry event object to provide more metadata and enhance the Sentry server's - * ability to match the error against release artifacts. + * Pre-processes a Sentry event object (in-place) to provide more metadata and enhance + * the Sentry server's ability to match the error against release artifacts. * @param event A Sentry-generated event */ - eventPreparer(event: any) { - event = this.pathFilter(event); - event = this.attachEventMetadata(event); + prepareEvent(event: any): boolean { + this.pathFilter(event); + this.attachEventMetadata(event); if(DEBUG) { console.log("DEBUG: event object for Sentry") console.log(event); - return null; //event + return false; //event } else { - return event; + return true; } } /** * Allows debugging our custom event preparation code without bombarding Sentry with errors * during development. + * + * Note that Sentry expects us either to return the event object to be sent or to return `null` + * if we want to prevent the event from being sent to the server. * @param event */ - __metaPreparer(event: any) { + prepareEventDebugWrapper(event: any) { if(DEBUG) { try { - return this.eventPreparer(event); + if(this.prepareEvent(event)) { + return event; + } else { + return null; + } } catch(err) { console.log(err); } } else { // If not in DEBUG mode, simply forward to the actual preparer; we should be notified // of any event-prep errors that may occur. - return this.eventPreparer(event); + if(this.prepareEvent(event)) { + return event; + } else { + return null; + } } } @@ -127,7 +137,7 @@ namespace com.keyman { // Do the actual Sentry initialization. //@ts-ignore Sentry.init({ - beforeSend: this.__metaPreparer.bind(this), + beforeSend: this.prepareEventDebugWrapper.bind(this), debug: DEBUG, dsn: 'https://cf96f32d107c4286ab2fd82af49c4d3b@sentry.keyman.com/11', // keyman-web DSN release: com.keyman.environment.SENTRY_RELEASE diff --git a/web/package.json b/web/package.json index b21b5bb91a..90612269af 100644 --- a/web/package.json +++ b/web/package.json @@ -18,7 +18,7 @@ }, "homepage": "https://github.com/keymanapp/keyman#readme", "devDependencies": { - "@keymanapp/sentry-manager": "^14.0.0", + "@keymanapp/web-sentry-manager": "^14.0.0", "@sentry/cli": "^1.52.3", "chai": "^4.2.0", "google-closure-compiler-java": "^20200224.0.0", diff --git a/web/source/keyboards/kmwkeyboards.ts b/web/source/keyboards/kmwkeyboards.ts index daf5928726..e6ef3cfa18 100644 --- a/web/source/keyboards/kmwkeyboards.ts +++ b/web/source/keyboards/kmwkeyboards.ts @@ -372,7 +372,7 @@ namespace com.keyman.keyboards { } this.doBeforeKeyboardChange(PInternalName,PLgCode); - let p = this._SetActiveKeyboard(PInternalName,PLgCode,true); + let p: Promise = this._SetActiveKeyboard(PInternalName,PLgCode,true); if(this.keymanweb.domManager.getLastActiveElement() != null) { this.keymanweb.domManager.focusLastActiveElement(); // TODO: Resolve without need for the cast. } @@ -384,6 +384,10 @@ namespace com.keyman.keyboards { this.doKeyboardChange(PInternalName, PLgCode); p.catch(function() { + // Rejection indicates a failure of the keyboard to load. + // + // In case p's rejection is never caught, throwing this error will generate logs that shows up + // in Sentry or in the console, with useful information for debugging either way. throw new Error("Unable to load keyboard with internal name \"" + PInternalName + "\", language code \"" + PLgCode + "\"."); }) diff --git a/web/testing/sentry-integration/index.html b/web/testing/sentry-integration/index.html index 43aa0488cf..3839d61fcb 100644 --- a/web/testing/sentry-integration/index.html +++ b/web/testing/sentry-integration/index.html @@ -34,7 +34,7 @@ --> - +