RN-289 Sentry integration (#873)
* Added JS code for Sentry * Removed leftover initializeSentry call * Added SentryOptions config setting * Added native components for react-native-exception-handler * Removed default props from ErrorText * Moved where Sentry is initialized * Added ios/sentry.properties to .gitignore * Added linking react-native-sentry to Fastlane * Fixed fastlane to include newlines in sentry.properties * Moved to manually link react-native-sentry * Captured redux errors with Sentry * Redid how Sentry is optionally compiled to be simpler * Added Sentry middleware to create redux breadcrumbs * Added Sentry tags for server version * Initialize Sentry when testing * Fixed string replacement for SentryEnabled in fastlane * Added react-native-sentry to NOTICE.txt
This commit is contained in:
parent
220fb588b9
commit
3ac7b48adc
21 changed files with 597 additions and 74 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -65,6 +65,7 @@ mattermost.keystore
|
|||
|
||||
# Sentry
|
||||
android/sentry.properties
|
||||
ios/sentry.properties
|
||||
|
||||
# Testing
|
||||
.nyc_output
|
||||
|
|
|
|||
39
NOTICE.txt
39
NOTICE.txt
|
|
@ -1295,8 +1295,6 @@ The MIT License (MIT)
|
|||
|
||||
Copyright (c) 2015 Bart Gryszko
|
||||
|
||||
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
|
|
@ -1304,13 +1302,9 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
|
|
@ -1351,3 +1345,36 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
---
|
||||
|
||||
## react-native-sentry
|
||||
|
||||
This product contains 'react-native-sentry', the Sentry SDK for React Native
|
||||
|
||||
* HOMEPAGE:
|
||||
* https://github.com/getsentry/react-native-sentry
|
||||
|
||||
* LICENSE:
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017 Sentry
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ import com.android.build.OutputFile
|
|||
*/
|
||||
|
||||
apply from: "../../node_modules/react-native/react.gradle"
|
||||
apply from: "../../node_modules/react-native-sentry/sentry.gradle"
|
||||
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
|
||||
|
||||
/**
|
||||
|
|
@ -144,6 +145,8 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':react-native-sentry')
|
||||
compile project(':react-native-exception-handler')
|
||||
compile project(':react-native-fetch-blob')
|
||||
compile fileTree(dir: "libs", include: ["*.jar"])
|
||||
compile "com.android.support:appcompat-v7:25.0.1"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import android.content.Context;
|
|||
import android.os.Bundle;
|
||||
|
||||
import com.facebook.react.ReactApplication;
|
||||
import io.sentry.RNSentryPackage;
|
||||
import com.masteratul.exceptionhandler.ReactNativeExceptionHandlerPackage;
|
||||
import com.RNFetchBlob.RNFetchBlobPackage;
|
||||
import com.gantix.JailMonkey.JailMonkeyPackage;
|
||||
import io.tradle.react.LocalAuthPackage;
|
||||
|
|
@ -60,7 +62,9 @@ public class MainApplication extends NavigationApplication implements INotificat
|
|||
new LocalAuthPackage(),
|
||||
new JailMonkeyPackage(),
|
||||
new RNFetchBlobPackage(),
|
||||
new MattermostManagedPackage()
|
||||
new MattermostManagedPackage(),
|
||||
new RNSentryPackage(this),
|
||||
new ReactNativeExceptionHandlerPackage()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
rootProject.name = 'Mattermost'
|
||||
include ':react-native-sentry'
|
||||
project(':react-native-sentry').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sentry/android')
|
||||
include ':react-native-exception-handler'
|
||||
project(':react-native-exception-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-exception-handler/android')
|
||||
include ':react-native-fetch-blob'
|
||||
project(':react-native-fetch-blob').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fetch-blob/android')
|
||||
include ':jail-monkey'
|
||||
|
|
|
|||
|
|
@ -14,12 +14,7 @@ import {makeStyleSheetFromTheme} from 'app/utils/theme';
|
|||
class ErrorText extends PureComponent {
|
||||
static propTypes = {
|
||||
error: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
theme: PropTypes.object
|
||||
};
|
||||
|
||||
static defaultProps = {
|
||||
error: {},
|
||||
theme: {}
|
||||
theme: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {
|
|||
Platform
|
||||
} from 'react-native';
|
||||
import DeviceInfo from 'react-native-device-info';
|
||||
import {setJSExceptionHandler} from 'react-native-exception-handler';
|
||||
import {setJSExceptionHandler, setNativeExceptionHandler} from 'react-native-exception-handler';
|
||||
import StatusBarSizeIOS from 'react-native-status-bar-size';
|
||||
import semver from 'semver';
|
||||
|
||||
|
|
@ -46,15 +46,18 @@ import PushNotifications from 'app/push_notifications';
|
|||
import {registerScreens} from 'app/screens';
|
||||
import configureStore from 'app/store';
|
||||
import mattermostManaged from 'app/mattermost_managed';
|
||||
import {
|
||||
captureException,
|
||||
initializeSentry,
|
||||
LOGGER_JAVASCRIPT,
|
||||
LOGGER_NATIVE
|
||||
} from 'app/utils/sentry';
|
||||
|
||||
import Config from 'assets/config';
|
||||
|
||||
const {StatusBarManager} = NativeModules;
|
||||
const store = configureStore(initialState);
|
||||
const AUTHENTICATION_TIMEOUT = 5 * 60 * 1000;
|
||||
|
||||
registerScreens(store, Provider);
|
||||
|
||||
export default class Mattermost {
|
||||
constructor() {
|
||||
if (Platform.OS === 'android') {
|
||||
|
|
@ -65,9 +68,13 @@ export default class Mattermost {
|
|||
}
|
||||
this.isConfigured = false;
|
||||
this.allowOtherServers = true;
|
||||
setJSExceptionHandler(this.errorHandler, false);
|
||||
|
||||
Orientation.lockToPortrait();
|
||||
this.unsubscribeFromStore = store.subscribe(this.listenForHydration);
|
||||
|
||||
initializeSentry();
|
||||
|
||||
this.store = configureStore(initialState);
|
||||
this.unsubscribeFromStore = this.store.subscribe(this.listenForHydration);
|
||||
AppState.addEventListener('change', this.handleAppStateChange);
|
||||
EventEmitter.on(General.CONFIG_CHANGED, this.handleConfigChanged);
|
||||
EventEmitter.on(NavigationTypes.NAVIGATION_RESET, this.handleReset);
|
||||
|
|
@ -87,12 +94,20 @@ export default class Mattermost {
|
|||
}
|
||||
);
|
||||
}
|
||||
|
||||
registerScreens(this.store, Provider);
|
||||
|
||||
setJSExceptionHandler(this.errorHandler, false);
|
||||
setNativeExceptionHandler(this.nativeErrorHandler);
|
||||
}
|
||||
|
||||
errorHandler = (e, isFatal) => {
|
||||
console.warn('Handling Javascript error ' + JSON.stringify(e)); // eslint-disable-line no-console
|
||||
captureException(e, LOGGER_JAVASCRIPT, this.store);
|
||||
|
||||
const intl = this.getIntl();
|
||||
closeWebSocket()(store.dispatch, store.getState);
|
||||
logError(e)(store.dispatch);
|
||||
closeWebSocket()(this.store.dispatch, this.store.getState);
|
||||
logError(e)(this.store.dispatch);
|
||||
|
||||
if (isFatal) {
|
||||
Alert.alert(
|
||||
|
|
@ -102,7 +117,7 @@ export default class Mattermost {
|
|||
text: intl.formatMessage({id: 'mobile.error_handler.button', defaultMessage: 'Relaunch'}),
|
||||
onPress: () => {
|
||||
// purge the store
|
||||
store.dispatch(purgeOfflineStore());
|
||||
this.store.dispatch(purgeOfflineStore());
|
||||
}
|
||||
}],
|
||||
{cancelable: false}
|
||||
|
|
@ -110,8 +125,13 @@ export default class Mattermost {
|
|||
}
|
||||
};
|
||||
|
||||
nativeErrorHandler = (e) => {
|
||||
console.warn('Handling native error ' + JSON.stringify(e)); // eslint-disable-line no-console
|
||||
captureException(e, LOGGER_NATIVE, this.store);
|
||||
};
|
||||
|
||||
getIntl = () => {
|
||||
const state = store.getState();
|
||||
const state = this.store.getState();
|
||||
let locale = DeviceInfo.getDeviceLocale().split('-')[0];
|
||||
if (state.views.i18n.locale) {
|
||||
locale = state.views.i18n.locale;
|
||||
|
|
@ -157,7 +177,7 @@ export default class Mattermost {
|
|||
};
|
||||
|
||||
handleAppStateChange = async (appState) => {
|
||||
const {dispatch, getState} = store;
|
||||
const {dispatch, getState} = this.store;
|
||||
const isActive = appState === 'active';
|
||||
setAppState(isActive)(dispatch, getState);
|
||||
|
||||
|
|
@ -211,7 +231,7 @@ export default class Mattermost {
|
|||
};
|
||||
|
||||
handleConfigChanged = async (serverVersion) => {
|
||||
const {dispatch, getState} = store;
|
||||
const {dispatch, getState} = this.store;
|
||||
const version = serverVersion.match(/^[0-9]*.[0-9]*.[0-9]*(-[a-zA-Z0-9.-]*)?/g)[0];
|
||||
const intl = this.getIntl();
|
||||
|
||||
|
|
@ -235,7 +255,7 @@ export default class Mattermost {
|
|||
};
|
||||
|
||||
handleManagedConfig = async (serverConfig) => {
|
||||
const {dispatch, getState} = store;
|
||||
const {dispatch, getState} = this.store;
|
||||
const state = getState();
|
||||
|
||||
let authNeeded = false;
|
||||
|
|
@ -328,15 +348,15 @@ export default class Mattermost {
|
|||
};
|
||||
|
||||
handleResetDisplayName = (displayName) => {
|
||||
store.dispatch(setChannelDisplayName(displayName));
|
||||
this.store.dispatch(setChannelDisplayName(displayName));
|
||||
};
|
||||
|
||||
handleStatusBarHeightChange = (nextStatusBarHeight) => {
|
||||
store.dispatch(setStatusBarHeight(nextStatusBarHeight));
|
||||
this.store.dispatch(setStatusBarHeight(nextStatusBarHeight));
|
||||
};
|
||||
|
||||
handleVersionUpgrade = async () => {
|
||||
const {dispatch, getState} = store;
|
||||
const {dispatch, getState} = this.store;
|
||||
|
||||
Client4.serverVersion = '';
|
||||
PushNotifications.setApplicationIconBadgeNumber(0);
|
||||
|
|
@ -352,7 +372,7 @@ export default class Mattermost {
|
|||
|
||||
// We need to wait for hydration to occur before load the router.
|
||||
listenForHydration = () => {
|
||||
const state = store.getState();
|
||||
const state = this.store.getState();
|
||||
if (state.views.root.hydrationComplete) {
|
||||
this.unsubscribeFromStore();
|
||||
|
||||
|
|
@ -386,7 +406,7 @@ export default class Mattermost {
|
|||
};
|
||||
|
||||
onRegisterDevice = (data) => {
|
||||
const {dispatch, getState} = store;
|
||||
const {dispatch, getState} = this.store;
|
||||
let prefix;
|
||||
if (Platform.OS === 'ios') {
|
||||
prefix = General.PUSH_NOTIFY_APPLE_REACT_NATIVE;
|
||||
|
|
@ -403,7 +423,7 @@ export default class Mattermost {
|
|||
|
||||
onPushNotification = (deviceNotification) => {
|
||||
const {data, foreground, message, userInfo, userInteraction} = deviceNotification;
|
||||
const {dispatch, getState} = store;
|
||||
const {dispatch, getState} = this.store;
|
||||
const state = getState();
|
||||
const notification = {
|
||||
data,
|
||||
|
|
@ -439,7 +459,7 @@ export default class Mattermost {
|
|||
};
|
||||
|
||||
onPushNotificationReply = (data, text, badge, completed) => {
|
||||
const {dispatch, getState} = store;
|
||||
const {dispatch, getState} = this.store;
|
||||
const state = getState();
|
||||
const {currentUserId} = state.entities.users;
|
||||
|
||||
|
|
@ -479,7 +499,7 @@ export default class Mattermost {
|
|||
};
|
||||
|
||||
resetBadgeAndVersion = () => {
|
||||
const {dispatch, getState} = store;
|
||||
const {dispatch, getState} = this.store;
|
||||
Client4.serverVersion = '';
|
||||
Client.serverVersion = '';
|
||||
Client.token = null;
|
||||
|
|
@ -492,7 +512,7 @@ export default class Mattermost {
|
|||
restartApp = async () => {
|
||||
Navigation.dismissModal({animationType: 'none'});
|
||||
|
||||
const {dispatch, getState} = store;
|
||||
const {dispatch, getState} = this.store;
|
||||
await loadConfigAndLicense()(dispatch, getState);
|
||||
this.startApp('fade');
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import EventEmitter from 'mattermost-redux/utils/event_emitter';
|
|||
|
||||
import {NavigationTypes, ViewTypes} from 'app/constants';
|
||||
import appReducer from 'app/reducers';
|
||||
import {createSentryMiddleware} from 'app/utils/sentry/middleware';
|
||||
|
||||
import {transformSet} from './utils';
|
||||
|
||||
|
|
@ -169,5 +170,7 @@ export default function configureAppStore(initialState) {
|
|||
}
|
||||
};
|
||||
|
||||
return configureStore(initialState, appReducer, offlineOptions, getAppReducer);
|
||||
return configureStore(initialState, appReducer, offlineOptions, getAppReducer, {
|
||||
additionalMiddleware: createSentryMiddleware()
|
||||
});
|
||||
}
|
||||
|
|
|
|||
162
app/utils/sentry/index.js
Normal file
162
app/utils/sentry/index.js
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {Platform} from 'react-native';
|
||||
import {Sentry} from 'react-native-sentry';
|
||||
|
||||
import Config from 'assets/config';
|
||||
|
||||
import {getConfig} from 'mattermost-redux/selectors/entities/general';
|
||||
import {getCurrentUser} from 'mattermost-redux/selectors/entities/users';
|
||||
import {getCurrentTeam, getCurrentTeamMembership} from 'mattermost-redux/selectors/entities/teams';
|
||||
import {getCurrentChannel, getMyCurrentChannelMembership} from 'mattermost-redux/selectors/entities/channels';
|
||||
|
||||
export const LOGGER_JAVASCRIPT = 'javascript';
|
||||
export const LOGGER_NATIVE = 'native';
|
||||
export const LOGGER_REDUX = 'redux';
|
||||
|
||||
export function initializeSentry() {
|
||||
if (!Config.SentryEnabled) {
|
||||
// Still allow Sentry to configure itself in case other code tries to call it
|
||||
Sentry.config('');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const dsn = getDsn();
|
||||
|
||||
if (!dsn) {
|
||||
console.warn('Sentry is enabled, but not configured on this platform'); // eslint-disable-line no-console
|
||||
}
|
||||
|
||||
Sentry.config(dsn, Config.SentryOptions).install();
|
||||
}
|
||||
|
||||
function getDsn() {
|
||||
if (Platform.OS === 'android') {
|
||||
return Config.SentryDsnAndroid;
|
||||
} else if (Platform.OS === 'ios') {
|
||||
return Config.SentryDsnIos;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
export function captureException(error, logger, store) {
|
||||
capture(() => {
|
||||
Sentry.captureException(error, {logger});
|
||||
}, store);
|
||||
}
|
||||
|
||||
export function captureMessage(message, logger, store) {
|
||||
capture(() => {
|
||||
Sentry.captureMessage(message, {logger});
|
||||
}, store);
|
||||
}
|
||||
|
||||
// Wrapper function to any calls to Sentry so that we can gather any necessary extra data
|
||||
// before sending.
|
||||
function capture(captureFunc, store) {
|
||||
if (!Config.SentryEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const state = store.getState();
|
||||
const config = getConfig(state);
|
||||
|
||||
// Don't contact Sentry if we're connected to a server with diagnostics disabled. Note that this will
|
||||
// still log if we're not connected to any server.
|
||||
if (config.EnableDiagnostics != null && config.EnableDiagnostics !== 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
Sentry.setUserContext(getUserContext(state));
|
||||
Sentry.setExtraContext(getExtraContext(state));
|
||||
Sentry.setTagsContext(getBuildTags(state));
|
||||
|
||||
console.warn('Capturing with Sentry at ' + getDsn() + '...'); // eslint-disable-line no-console
|
||||
|
||||
captureFunc();
|
||||
} catch (e) {
|
||||
// Don't want this to get into an infinite loop again...
|
||||
console.warn('Exception occured while sending to Sentry'); // eslint-disable-line no-console
|
||||
console.warn(e); // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
function getUserContext(state) {
|
||||
const currentUser = getCurrentUser(state);
|
||||
|
||||
if (!currentUser) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
userID: currentUser.id, // This can be changed to id after we upgrade to Sentry >= 0.14.10,
|
||||
email: '',
|
||||
username: '',
|
||||
extra: {
|
||||
locale: currentUser.locale,
|
||||
roles: currentUser.roles
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function getExtraContext(state) {
|
||||
const context = {};
|
||||
|
||||
const currentTeam = getCurrentTeam(state);
|
||||
if (currentTeam) {
|
||||
context.currentTeam = {
|
||||
id: currentTeam.id
|
||||
};
|
||||
}
|
||||
|
||||
const currentTeamMember = getCurrentTeamMembership(state);
|
||||
if (currentTeamMember) {
|
||||
context.currentTeamMember = {
|
||||
roles: currentTeamMember.roles
|
||||
};
|
||||
}
|
||||
|
||||
const currentChannel = getCurrentChannel(state);
|
||||
if (currentChannel) {
|
||||
context.currentChannel = {
|
||||
id: currentChannel.id,
|
||||
type: currentChannel.type
|
||||
};
|
||||
}
|
||||
|
||||
const currentChannelMember = getMyCurrentChannelMembership(state);
|
||||
if (currentChannelMember) {
|
||||
context.currentChannelMember = {
|
||||
roles: currentChannelMember.roles
|
||||
};
|
||||
}
|
||||
|
||||
const config = getConfig(state);
|
||||
if (config) {
|
||||
context.config = {
|
||||
BuildDate: config.BuildDate,
|
||||
BuildEnterpriseReady: config.BuildEnterpriseReady,
|
||||
BuildHash: config.BuildHash,
|
||||
BuildHashEnterprise: config.BuildHashEnterprise,
|
||||
BuildNumber: config.BuildNumber
|
||||
};
|
||||
}
|
||||
|
||||
return context;
|
||||
}
|
||||
|
||||
function getBuildTags(state) {
|
||||
const tags = {};
|
||||
|
||||
const config = getConfig(state);
|
||||
if (config) {
|
||||
tags.serverBuildHash = config.BuildHash;
|
||||
tags.serverBuildNumber = config.BuildNumber;
|
||||
}
|
||||
|
||||
return tags;
|
||||
}
|
||||
43
app/utils/sentry/middleware.js
Normal file
43
app/utils/sentry/middleware.js
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {Sentry} from 'react-native-sentry';
|
||||
import {BATCH} from 'redux-batched-actions';
|
||||
|
||||
export const BREADCRUMB_REDUX_ACTION = 'redux-action';
|
||||
|
||||
export function createSentryMiddleware() {
|
||||
return (store) => { // eslint-disable-line no-unused-vars
|
||||
return (next) => {
|
||||
return (action) => {
|
||||
Sentry.captureBreadcrumb(makeBreadcrumbFromAction(action));
|
||||
|
||||
return next(action);
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function makeBreadcrumbFromAction(action) {
|
||||
const breadcrumb = {
|
||||
category: BREADCRUMB_REDUX_ACTION,
|
||||
message: action.type
|
||||
};
|
||||
|
||||
if (action.type === BATCH) {
|
||||
// Attach additional information so that batched actions display what they're doing
|
||||
breadcrumb.data = action.payload.map((a) => a.type);
|
||||
}
|
||||
|
||||
if (action.type === BATCH) {
|
||||
// Attach additional information so that batched actions display what they're doing, and make it
|
||||
// into an object because that's what is expected
|
||||
breadcrumb.data = {};
|
||||
|
||||
action.payload.forEach((a, index) => {
|
||||
breadcrumb.data[index] = a.type;
|
||||
});
|
||||
}
|
||||
|
||||
return breadcrumb;
|
||||
}
|
||||
|
|
@ -6,5 +6,16 @@
|
|||
"MinServerVersion": "4.0.0",
|
||||
"PlatformNoticeURL": "https://about.mattermost.com/platform-notice-txt/",
|
||||
"MobileNoticeURL": "https://about.mattermost.com/mobile-notice-txt/",
|
||||
"SegmentApiKey": "3MT7rAoC0OP7yy3ThzqFSAtKzmzqtUPX"
|
||||
"SegmentApiKey": "3MT7rAoC0OP7yy3ThzqFSAtKzmzqtUPX",
|
||||
|
||||
"SentryEnabled": false,
|
||||
"SentryDsnIos": "",
|
||||
"SentryDsnAndroid": "",
|
||||
"SentryOptions": {
|
||||
"deactivateStacktraceMerging": true,
|
||||
"autoBreadcrumbs": {
|
||||
"xhr": false,
|
||||
"console": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,13 +46,7 @@ platform :ios do
|
|||
lane :beta do
|
||||
match(type: 'appstore', app_identifier: 'com.mattermost.rnbeta')
|
||||
|
||||
if ENV['SEGMENT_API_KEY']
|
||||
find_replace_string(
|
||||
path_to_file: './dist/assets/config.json',
|
||||
old_string: '"SegmentApiKey": "3MT7rAoC0OP7yy3ThzqFSAtKzmzqtUPX"',
|
||||
new_string: "\"SegmentApiKey\": \"#{ENV['SEGMENT_API_KEY']}\""
|
||||
)
|
||||
end
|
||||
configure_from_env()
|
||||
|
||||
build_ios({
|
||||
release: true,
|
||||
|
|
@ -98,13 +92,7 @@ platform :ios do
|
|||
lane :release do
|
||||
match(type: 'appstore', app_identifier: 'com.mattermost.rn')
|
||||
|
||||
if ENV['SEGMENT_API_KEY']
|
||||
find_replace_string(
|
||||
path_to_file: './dist/assets/config.json',
|
||||
old_string: '"SegmentApiKey": "3MT7rAoC0OP7yy3ThzqFSAtKzmzqtUPX"',
|
||||
new_string: "\"SegmentApiKey\": \"#{ENV['SEGMENT_API_KEY']}\""
|
||||
)
|
||||
end
|
||||
configure_from_env()
|
||||
|
||||
# snapshot
|
||||
|
||||
|
|
@ -259,13 +247,7 @@ platform :android do
|
|||
|
||||
desc 'Submit a new Beta Build to Google Play'
|
||||
lane :alpha do
|
||||
if ENV['SEGMENT_API_KEY']
|
||||
find_replace_string(
|
||||
path_to_file: './dist/assets/config.json',
|
||||
old_string: '"SegmentApiKey": "3MT7rAoC0OP7yy3ThzqFSAtKzmzqtUPX"',
|
||||
new_string: "\"SegmentApiKey\": \"#{ENV['SEGMENT_API_KEY']}\""
|
||||
)
|
||||
end
|
||||
configure_from_env()
|
||||
|
||||
build_android({
|
||||
release: true,
|
||||
|
|
@ -324,7 +306,6 @@ platform :android do
|
|||
new_string: 'return false;'
|
||||
)
|
||||
|
||||
|
||||
find_replace_string(
|
||||
path_to_file: './android/app/src/main/java/com/mattermost/rn/MainApplication.java',
|
||||
old_string: 'package com.mattermost.rnbeta;',
|
||||
|
|
@ -385,13 +366,7 @@ platform :android do
|
|||
new_string: 'Mattermost;'
|
||||
)
|
||||
|
||||
if ENV['SEGMENT_API_KEY']
|
||||
find_replace_string(
|
||||
path_to_file: './dist/assets/config.json',
|
||||
old_string: '"SegmentApiKey": "3MT7rAoC0OP7yy3ThzqFSAtKzmzqtUPX"',
|
||||
new_string: "\"SegmentApiKey\": \"#{ENV['SEGMENT_API_KEY']}\""
|
||||
)
|
||||
end
|
||||
configure_from_env()
|
||||
|
||||
build_android({
|
||||
release: true,
|
||||
|
|
@ -449,6 +424,8 @@ platform :android do
|
|||
)
|
||||
end
|
||||
|
||||
link_sentry_android()
|
||||
|
||||
gradle(
|
||||
task: 'assemble',
|
||||
build_type: (options[:release] ? 'Release' : 'Debug'),
|
||||
|
|
@ -552,3 +529,78 @@ platform :android do
|
|||
)
|
||||
end
|
||||
end
|
||||
|
||||
def configure_from_env()
|
||||
if ENV['SEGMENT_API_KEY']
|
||||
find_replace_string(
|
||||
path_to_file: './dist/assets/config.json',
|
||||
old_string: '"SegmentApiKey": "3MT7rAoC0OP7yy3ThzqFSAtKzmzqtUPX"',
|
||||
new_string: "\"SegmentApiKey\": \"#{ENV['SEGMENT_API_KEY']}\""
|
||||
)
|
||||
end
|
||||
|
||||
if ENV['SENTRY_ENABLED']
|
||||
find_replace_string(
|
||||
path_to_file: './dist/assets/config.json',
|
||||
old_string: '"SentryEnabled": false',
|
||||
new_string: "\"SentryEnabled\": #{ENV['SENTRY_ENABLED']}"
|
||||
)
|
||||
end
|
||||
|
||||
if ENV['SENTRY_ORG']
|
||||
find_replace_string(
|
||||
path_to_file: './dist/assets/config.json',
|
||||
old_string: '"SentryOrg": ""',
|
||||
new_string: "\"SentryOrg\": \"#{ENV['SENTRY_ORG']}\""
|
||||
)
|
||||
end
|
||||
|
||||
if ENV['SENTRY_PROJECT_IOS']
|
||||
find_replace_string(
|
||||
path_to_file: './dist/assets/config.json',
|
||||
old_string: '"SentryProjectIos": ""',
|
||||
new_string: "\"SentryProjectIos\": \"#{ENV['SENTRY_PROJECT_IOS']}\""
|
||||
)
|
||||
end
|
||||
|
||||
if ENV['SENTRY_PROJECT_ANDROID']
|
||||
find_replace_string(
|
||||
path_to_file: './dist/assets/config.json',
|
||||
old_string: '"SentryProjectAndroid": ""',
|
||||
new_string: "\"SentryProjectAndroid\": \"#{ENV['SENTRY_PROJECT_ANDROID']}\""
|
||||
)
|
||||
end
|
||||
|
||||
if ENV['SENTRY_DSN_IOS']
|
||||
find_replace_string(
|
||||
path_to_file: './dist/assets/config.json',
|
||||
old_string: '"SentryDsnIos": ""',
|
||||
new_string: "\"SentryDsnIos\": \"#{ENV['SENTRY_DSN_IOS']}\""
|
||||
)
|
||||
end
|
||||
|
||||
if ENV['SENTRY_DSN_ANDROID']
|
||||
find_replace_string(
|
||||
path_to_file: './dist/assets/config.json',
|
||||
old_string: '"SentryDsnAndroid": ""',
|
||||
new_string: "\"SentryDsnAndroid\": \"#{ENV['SENTRY_DSN_ANDROID']}\""
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def link_sentry_android()
|
||||
if ENV['SENTRY_ENABLED'] == 'true'
|
||||
File.open('../android/sentry.properties', 'w+') do |f|
|
||||
UI.message('Creating sentry.properties from environment')
|
||||
f.write(
|
||||
'defaults.url=https://sentry.io/\n'\
|
||||
"defaults.org=#{ENV['SENTRY_ORG']}\n"\
|
||||
"defaults.project=#{ENV['SENTRY_PROJECT_ANDROID']}\n"\
|
||||
"auth.token=#{ENV['SENTRY_AUTH_TOKEN']}\n"\
|
||||
"cli.executable=${File.expand_path('node_modules/sentry-cli-binary/bin/sentry-cli')}\n"
|
||||
)
|
||||
end
|
||||
else
|
||||
UI.message('Not creating sentry.properties because Sentry is disabled')
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
// See License.txt for license information.
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
// import {AppRegistry} from 'react-native';
|
||||
import Mattermost from 'app/mattermost';
|
||||
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@
|
|||
F006936FC2884C24A1321FC0 /* MaterialIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 356C9186FA374641A00EB2EA /* MaterialIcons.ttf */; };
|
||||
F083DB472349411A8E6E7AAD /* OpenSans-LightItalic.ttf in Resources */ = {isa = PBXBuildFile; fileRef = BE17F630DB5D41FD93F32D22 /* OpenSans-LightItalic.ttf */; };
|
||||
FDEF24E9D9AB47728E11033B /* libRNPasscodeStatus.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 17A75F5A3D0D4A4A995DCD76 /* libRNPasscodeStatus.a */; };
|
||||
1B5204C4347D45A9A7AC3EA5 /* libReactNativeExceptionHandler.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A22BC320BA04E18A7F2A4E6 /* libReactNativeExceptionHandler.a */; };
|
||||
CB1B312483534997BEE7E65E /* libRNSentry.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2AE23B96BF7545B9A77C0C87 /* libRNSentry.a */; };
|
||||
DEBCF44F1F46413BB407D316 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 8606EB1EB7E349EF8248933E /* libz.tbd */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
|
@ -482,6 +485,11 @@
|
|||
EE671DF7637347CD8C069819 /* libRNDeviceInfo.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNDeviceInfo.a; sourceTree = "<group>"; };
|
||||
F1F071EE85494E269A50AE88 /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = SimpleLineIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = "<group>"; };
|
||||
FBBEC29EE2D3418D9AC33BD5 /* OpenSans-ExtraBoldItalic.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = "OpenSans-ExtraBoldItalic.ttf"; path = "../assets/fonts/OpenSans-ExtraBoldItalic.ttf"; sourceTree = "<group>"; };
|
||||
3B1E210BF3B649BBBF427977 /* ReactNativeExceptionHandler.xcodeproj */ = {isa = PBXFileReference; name = "ReactNativeExceptionHandler.xcodeproj"; path = "../node_modules/react-native-exception-handler/ios/ReactNativeExceptionHandler.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
|
||||
4A22BC320BA04E18A7F2A4E6 /* libReactNativeExceptionHandler.a */ = {isa = PBXFileReference; name = "libReactNativeExceptionHandler.a"; path = "libReactNativeExceptionHandler.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
|
||||
9552041247E749308CE7B412 /* RNSentry.xcodeproj */ = {isa = PBXFileReference; name = "RNSentry.xcodeproj"; path = "../node_modules/react-native-sentry/ios/RNSentry.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
|
||||
2AE23B96BF7545B9A77C0C87 /* libRNSentry.a */ = {isa = PBXFileReference; name = "libRNSentry.a"; path = "libRNSentry.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
|
||||
8606EB1EB7E349EF8248933E /* libz.tbd */ = {isa = PBXFileReference; name = "libz.tbd"; path = "usr/lib/libz.tbd"; sourceTree = SDKROOT; fileEncoding = undefined; lastKnownFileType = sourcecode.text-based-dylib-definition; explicitFileType = undefined; includeInIndex = 0; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
|
@ -525,6 +533,9 @@
|
|||
38FB8B0A3AAD4F839EF6EF0B /* libJailMonkey.a in Frameworks */,
|
||||
1C1F002AC5DE465FAE9E0D74 /* libFastImage.a in Frameworks */,
|
||||
2B2679D44CFA48E6A60D68BC /* libRNFetchBlob.a in Frameworks */,
|
||||
1B5204C4347D45A9A7AC3EA5 /* libReactNativeExceptionHandler.a in Frameworks */,
|
||||
CB1B312483534997BEE7E65E /* libRNSentry.a in Frameworks */,
|
||||
DEBCF44F1F46413BB407D316 /* libz.tbd in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
@ -867,6 +878,8 @@
|
|||
27A6EA89298440439DA9F98D /* JailMonkey.xcodeproj */,
|
||||
50ECB1D221E44F51B5690DF2 /* FastImage.xcodeproj */,
|
||||
546CB0A5BB8F453890CBA559 /* RNFetchBlob.xcodeproj */,
|
||||
3B1E210BF3B649BBBF427977 /* ReactNativeExceptionHandler.xcodeproj */,
|
||||
9552041247E749308CE7B412 /* RNSentry.xcodeproj */,
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -903,6 +916,15 @@
|
|||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
08742915873A4636AD1EC89C /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
8606EB1EB7E349EF8248933E /* libz.tbd */,
|
||||
);
|
||||
name = Frameworks;
|
||||
path = Application;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
|
|
@ -933,6 +955,7 @@
|
|||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||
37DA4BA41E6F55AD002B058E /* Embed Frameworks */,
|
||||
AE4769B235D14E6C9C64EA78 /* Upload Debug Symbols to Sentry */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
|
|
@ -1476,7 +1499,21 @@
|
|||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/packager/react-native-xcode.sh\n\n";
|
||||
shellScript = "./bundleReactNative.sh";
|
||||
};
|
||||
AE4769B235D14E6C9C64EA78 /* Upload Debug Symbols to Sentry */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
name = "Upload Debug Symbols to Sentry";
|
||||
inputPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "./uploadDebugSymbols.sh";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
|
|
@ -1573,6 +1610,8 @@
|
|||
"$(SRCROOT)/../node_modules/react-native/Libraries/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-fast-image/ios/FastImage/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-fetch-blob/ios/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-exception-handler/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-sentry/ios/**",
|
||||
);
|
||||
INFOPLIST_FILE = Mattermost/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
|
|
@ -1613,6 +1652,8 @@
|
|||
"$(SRCROOT)/../node_modules/react-native/Libraries/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-fast-image/ios/FastImage/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-fetch-blob/ios/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-exception-handler/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-sentry/ios/**",
|
||||
);
|
||||
INFOPLIST_FILE = Mattermost/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
|
|
|
|||
|
|
@ -10,6 +10,11 @@
|
|||
#import "AppDelegate.h"
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTRootView.h>
|
||||
#if __has_include(<React/RNSentry.h>)
|
||||
#import <React/RNSentry.h> // This is used for versions of react >= 0.40
|
||||
#else
|
||||
#import "RNSentry.h" // This is used for versions of react < 0.40
|
||||
#endif
|
||||
#import "Orientation.h"
|
||||
#import "RCCManager.h"
|
||||
#import "RNNotifications.h"
|
||||
|
|
|
|||
15
ios/bundleReactNative.sh
Executable file
15
ios/bundleReactNative.sh
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
|
||||
export NODE_BINARY=node
|
||||
|
||||
if [[ "${SENTRY_ENABLED}" = "true" ]]; then
|
||||
echo "Sentry native integration is enabled"
|
||||
|
||||
./makeSentryProperties.sh
|
||||
|
||||
export SENTRY_PROPERTIES=sentry.properties
|
||||
../node_modules/sentry-cli-binary/bin/sentry-cli react-native xcode ../node_modules/react-native/packager/react-native-xcode.sh
|
||||
else
|
||||
echo "Sentry native integration is not enabled"
|
||||
../node_modules/react-native/packager/react-native-xcode.sh
|
||||
fi
|
||||
19
ios/makeSentryProperties.sh
Executable file
19
ios/makeSentryProperties.sh
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
|
||||
sentry_properties="defaults.url=https://sentry.io
|
||||
defaults.org=${SENTRY_ORG}
|
||||
defaults.project=${SENTRY_PROJECT_IOS}
|
||||
auth.token=${SENTRY_AUTH_TOKEN}
|
||||
cli.executable=../node_modules/sentry-cli-binary/bin/sentry-cli"
|
||||
|
||||
if [[ "${SENTRY_ENABLED}" = "true" ]]; then
|
||||
if [[ ! -f "sentry.properties" ]]; then
|
||||
echo "Creating sentry.properties from environment"
|
||||
|
||||
echo "${sentry_properties}" > sentry.properties
|
||||
else
|
||||
echo "sentry.properties already exists"
|
||||
fi
|
||||
else
|
||||
echo "Not creating sentry.properties because Sentry is disabled"
|
||||
fi
|
||||
14
ios/uploadDebugSymbols.sh
Executable file
14
ios/uploadDebugSymbols.sh
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
|
||||
export SENTRY_PROPERTIES=sentry.properties
|
||||
|
||||
if [[ "${SENTRY_ENABLED}" = "true" ]]; then
|
||||
echo "Uploading debugging symbols to Sentry"
|
||||
|
||||
./makeSentryProperties.sh
|
||||
|
||||
export SENTRY_PROPERTIES=sentry.properties
|
||||
../node_modules/sentry-cli-binary/bin/sentry-cli upload-dsym
|
||||
else
|
||||
echo "Not uploading debugging symbols to Sentry because Sentry is disabled"
|
||||
fi
|
||||
|
|
@ -26,9 +26,9 @@
|
|||
"react-native-cookies": "3.1.0",
|
||||
"react-native-device-info": "0.11.0",
|
||||
"react-native-drawer": "2.3.0",
|
||||
"react-native-exception-handler": "1.1.0",
|
||||
"react-native-fetch-blob": "0.10.8",
|
||||
"react-native-exception-handler": "2.0.1",
|
||||
"react-native-fast-image": "1.0.0",
|
||||
"react-native-fetch-blob": "0.10.8",
|
||||
"react-native-image-picker": "jp928/react-native-image-picker#6ee35b69f3dbd6c7c66f580fd4d9eabf398703d4",
|
||||
"react-native-keyboard-aware-scroll-view": "0.2.8",
|
||||
"react-native-linear-gradient": "2.0.0",
|
||||
|
|
@ -37,6 +37,7 @@
|
|||
"react-native-notifications": "enahum/react-native-notifications.git",
|
||||
"react-native-orientation": "enahum/react-native-orientation.git",
|
||||
"react-native-passcode-status": "1.1.0",
|
||||
"react-native-sentry": "0.14.5",
|
||||
"react-native-status-bar-size": "0.3.2",
|
||||
"react-native-svg": "5.1.8",
|
||||
"react-native-swiper": "1.5.4",
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import register from 'babel-core/register';
|
|||
import chai from 'chai';
|
||||
import chaiEnzyme from 'chai-enzyme';
|
||||
import ReactNative from 'react-native-mock';
|
||||
import {Sentry} from 'react-native-sentry';
|
||||
|
||||
const m = require('module');
|
||||
const originalLoader = m._load;
|
||||
|
|
@ -59,3 +60,6 @@ config.ignore = function(filename) {
|
|||
register(config);
|
||||
|
||||
chai.use(chaiEnzyme());
|
||||
|
||||
// Initialize Sentry so that it doesn't complain when the middleware tries to create breadcrumbs
|
||||
Sentry.config('');
|
||||
|
|
|
|||
108
yarn.lock
108
yarn.lock
|
|
@ -1439,6 +1439,12 @@ cli-cursor@^1.0.1:
|
|||
dependencies:
|
||||
restore-cursor "^1.0.1"
|
||||
|
||||
cli-cursor@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
|
||||
dependencies:
|
||||
restore-cursor "^2.0.0"
|
||||
|
||||
cli-width@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.1.0.tgz#b234ca209b29ef66fc518d9b98d5847b00edf00a"
|
||||
|
|
@ -2309,6 +2315,14 @@ external-editor@^1.0.1:
|
|||
spawn-sync "^1.0.15"
|
||||
tmp "^0.0.29"
|
||||
|
||||
external-editor@^2.0.1:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.4.tgz#1ed9199da9cbfe2ef2f7a31b2fde8b0d12368972"
|
||||
dependencies:
|
||||
iconv-lite "^0.4.17"
|
||||
jschardet "^1.4.2"
|
||||
tmp "^0.0.31"
|
||||
|
||||
extglob@^0.3.1:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
|
||||
|
|
@ -2382,6 +2396,12 @@ figures@^1.3.5:
|
|||
escape-string-regexp "^1.0.5"
|
||||
object-assign "^4.1.0"
|
||||
|
||||
figures@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
|
||||
dependencies:
|
||||
escape-string-regexp "^1.0.5"
|
||||
|
||||
file-entry-cache@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361"
|
||||
|
|
@ -2860,6 +2880,10 @@ iconv-lite@0.4.15:
|
|||
version "0.4.15"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb"
|
||||
|
||||
iconv-lite@^0.4.17:
|
||||
version "0.4.18"
|
||||
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.18.tgz#23d8656b16aae6742ac29732ea8f0336a4789cf2"
|
||||
|
||||
ignore@^3.2.0:
|
||||
version "3.2.7"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.2.7.tgz#4810ca5f1d8eca5595213a34b94f2eb4ed926bbd"
|
||||
|
|
@ -2914,6 +2938,24 @@ inquirer@1.1.3:
|
|||
strip-ansi "^3.0.0"
|
||||
through "^2.3.6"
|
||||
|
||||
inquirer@3.0.6:
|
||||
version "3.0.6"
|
||||
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.0.6.tgz#e04aaa9d05b7a3cb9b0f407d04375f0447190347"
|
||||
dependencies:
|
||||
ansi-escapes "^1.1.0"
|
||||
chalk "^1.0.0"
|
||||
cli-cursor "^2.1.0"
|
||||
cli-width "^2.0.0"
|
||||
external-editor "^2.0.1"
|
||||
figures "^2.0.0"
|
||||
lodash "^4.3.0"
|
||||
mute-stream "0.0.7"
|
||||
run-async "^2.2.0"
|
||||
rx "^4.1.0"
|
||||
string-width "^2.0.0"
|
||||
strip-ansi "^3.0.0"
|
||||
through "^2.3.6"
|
||||
|
||||
inquirer@^0.12.0:
|
||||
version "0.12.0"
|
||||
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e"
|
||||
|
|
@ -3271,6 +3313,10 @@ jsbn@~0.1.0:
|
|||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
|
||||
|
||||
jschardet@^1.4.2:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.5.1.tgz#c519f629f86b3a5bedba58a88d311309eec097f9"
|
||||
|
||||
jsdom-global@3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/jsdom-global/-/jsdom-global-3.0.2.tgz#6bd299c13b0c4626b2da2c0393cd4385d606acb9"
|
||||
|
|
@ -3769,6 +3815,10 @@ mime@1.3.4, mime@^1.3.4:
|
|||
version "1.3.4"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53"
|
||||
|
||||
mimic-fn@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18"
|
||||
|
||||
min-document@^2.19.0:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
|
||||
|
|
@ -3870,6 +3920,10 @@ mute-stream@0.0.6:
|
|||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.6.tgz#48962b19e169fd1dfc240b3f1e7317627bbc47db"
|
||||
|
||||
mute-stream@0.0.7:
|
||||
version "0.0.7"
|
||||
resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
|
||||
|
||||
nan@^2.3.0:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.2.tgz#e4ff34e6c95fdfb5aecc08de6596f43605a7db45"
|
||||
|
|
@ -4080,6 +4134,12 @@ onetime@^1.0.0:
|
|||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789"
|
||||
|
||||
onetime@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
|
||||
dependencies:
|
||||
mimic-fn "^1.0.0"
|
||||
|
||||
opn@^3.0.2:
|
||||
version "3.0.3"
|
||||
resolved "https://registry.yarnpkg.com/opn/-/opn-3.0.3.tgz#b6d99e7399f78d65c3baaffef1fb288e9b85243a"
|
||||
|
|
@ -4289,6 +4349,10 @@ process@~0.5.1:
|
|||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf"
|
||||
|
||||
progress@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f"
|
||||
|
||||
progress@^1.1.8:
|
||||
version "1.1.8"
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
|
||||
|
|
@ -4370,6 +4434,10 @@ range-parser@~1.2.0:
|
|||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e"
|
||||
|
||||
raven-js@^3.16.1:
|
||||
version "3.17.0"
|
||||
resolved "https://registry.yarnpkg.com/raven-js/-/raven-js-3.17.0.tgz#779457ac7910512c3c2cc9bb6d0a9eeb59a969ec"
|
||||
|
||||
raw-body@~2.1.2:
|
||||
version "2.1.7"
|
||||
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.1.7.tgz#adfeace2e4fb3098058014d08c072dcc59758774"
|
||||
|
|
@ -4508,9 +4576,9 @@ react-native-drawer@2.3.0:
|
|||
dependencies:
|
||||
tween-functions "^1.0.1"
|
||||
|
||||
react-native-exception-handler@1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-exception-handler/-/react-native-exception-handler-1.1.0.tgz#320b7fc9c104e11d66173a2b0daeac97831a1a37"
|
||||
react-native-exception-handler@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/react-native-exception-handler/-/react-native-exception-handler-2.0.1.tgz#4ab9fc668927c2672adfffce3784316d81f130e8"
|
||||
|
||||
react-native-fetch-blob@0.10.8:
|
||||
version "0.10.8"
|
||||
|
|
@ -4598,6 +4666,17 @@ react-native-passcode-status@1.1.0:
|
|||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-native-passcode-status/-/react-native-passcode-status-1.1.0.tgz#e5922d5f4d79bc09849438d620406e5ccd31168a"
|
||||
|
||||
react-native-sentry@0.14.5:
|
||||
version "0.14.5"
|
||||
resolved "https://registry.yarnpkg.com/react-native-sentry/-/react-native-sentry-0.14.5.tgz#477aeca99a4c36326a64ca575e2b3718a3fb05a6"
|
||||
dependencies:
|
||||
chalk "^1.1.1"
|
||||
glob "7.1.1"
|
||||
inquirer "3.0.6"
|
||||
raven-js "^3.16.1"
|
||||
sentry-cli-binary "^1.16.0"
|
||||
xcode "0.9.3"
|
||||
|
||||
react-native-status-bar-size@0.3.2:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.yarnpkg.com/react-native-status-bar-size/-/react-native-status-bar-size-0.3.2.tgz#e3ffd906f021220c256857a8de6b945f8ea3fabc"
|
||||
|
|
@ -5098,6 +5177,13 @@ restore-cursor@^1.0.1:
|
|||
exit-hook "^1.0.0"
|
||||
onetime "^1.0.0"
|
||||
|
||||
restore-cursor@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
|
||||
dependencies:
|
||||
onetime "^2.0.0"
|
||||
signal-exit "^3.0.2"
|
||||
|
||||
right-align@^0.1.1:
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef"
|
||||
|
|
@ -5285,6 +5371,12 @@ send@0.15.1:
|
|||
range-parser "~1.2.0"
|
||||
statuses "~1.3.1"
|
||||
|
||||
sentry-cli-binary@^1.16.0:
|
||||
version "1.19.1"
|
||||
resolved "https://registry.yarnpkg.com/sentry-cli-binary/-/sentry-cli-binary-1.19.1.tgz#0abc210ed7f35d7ec0680963e612b15bd2bc14c0"
|
||||
dependencies:
|
||||
progress "2.0.0"
|
||||
|
||||
serialize-error@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-2.1.0.tgz#50b679d5635cdf84667bdc8e59af4e5b81d5f60a"
|
||||
|
|
@ -5368,7 +5460,7 @@ signal-exit@^2.0.0:
|
|||
version "2.1.2"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-2.1.2.tgz#375879b1f92ebc3b334480d038dc546a6d558564"
|
||||
|
||||
signal-exit@^3.0.0, signal-exit@^3.0.1:
|
||||
signal-exit@^3.0.0, signal-exit@^3.0.1, signal-exit@^3.0.2:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
|
||||
|
||||
|
|
@ -5716,6 +5808,12 @@ tmp@^0.0.29:
|
|||
dependencies:
|
||||
os-tmpdir "~1.0.1"
|
||||
|
||||
tmp@^0.0.31:
|
||||
version "0.0.31"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.31.tgz#8f38ab9438e17315e5dbd8b3657e8bfb277ae4a7"
|
||||
dependencies:
|
||||
os-tmpdir "~1.0.1"
|
||||
|
||||
tmpl@1.0.x:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1"
|
||||
|
|
@ -6050,7 +6148,7 @@ ws@^2.0.3:
|
|||
safe-buffer "~5.0.1"
|
||||
ultron "~1.1.0"
|
||||
|
||||
xcode@^0.9.1:
|
||||
xcode@0.9.3, xcode@^0.9.1:
|
||||
version "0.9.3"
|
||||
resolved "https://registry.yarnpkg.com/xcode/-/xcode-0.9.3.tgz#910a89c16aee6cc0b42ca805a6d0b4cf87211cf3"
|
||||
dependencies:
|
||||
|
|
|
|||
Loading…
Reference in a new issue