Skip to content

Commit 72b2fde

Browse files
authored
Merge pull request #395 from hakaari/feat/unified-logger
2 parents c7c3500 + d916fe6 commit 72b2fde

20 files changed

Lines changed: 1619 additions & 192 deletions

README.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,9 @@ Each test file requires an `appId` block and a `---` separator before the comman
190190
appId: com.pears.pass
191191
---
192192
- launchApp
193-
- assertVisible: 'Master password'
193+
- assertVisible: "Master password"
194194
- tapOn:
195-
text: 'Master password'
195+
text: "Master password"
196196
```
197197
198198
For more information, see the [Maestro documentation](https://maestro.mobile.dev/).
@@ -230,6 +230,31 @@ We welcome contributions. See [`CONTRIBUTING.md`](./CONTRIBUTING.md) for the dev
230230

231231
---
232232

233+
## Logging
234+
235+
Logging is off by default. When enabled, logs are written to the app's cache directory — `main.log` from the JS host (React Native side) and `core-logs.txt` from the Bare vault worker. The worker's sink redacts known sensitive fields (passwords, keys, tokens, etc.) before writing to `core-logs.txt`. The host logger does not redact, so treat anything passed to `logger.*` on the JS side as on-disk-visible in `main.log`.
236+
237+
Two ways to enable:
238+
239+
- **In-app toggle** (Settings → Diagnostics → **Enable logs**). Persists across launches. Toggling off stops writes but keeps existing log files; toggling back on resumes appending to the same files, so a session can span multiple toggles.
240+
- **Nightly builds** (`PearPass-nightly`): logging defaults to `debug` on first launch so testers don't have to opt in. The toggle still works to disable it.
241+
242+
Logs can be shared via Diagnostics screen **Share logs** action that zips both files plus a small metadata file (app version, distribution channel).
243+
244+
---
245+
246+
## Error reporting
247+
248+
**PearPass mobile is open source. Public releases and self-built versions never send any data anywhere. Sentry is only enabled on our nightly distribution channel for catching crashes during pre-release testing.**
249+
250+
Verifying:
251+
252+
- The gate is `isNightly()` from `src/constants/distribution.js`. Returns `false` unless the distribution channel is `nightly`.
253+
- The Expo config plugin for Sentry is only loaded when `PEARPASS_DISTRIBUTION=nightly` at build time. `app.config.ts`. `app.json` has no Sentry plugin entry, so standard / F-Droid builds never include it.
254+
- The Bare-side Sentry SDK (`sentry-bare`) is an optional peer dependency of `pearpass-lib-vault-core` — public builds don't install it.
255+
256+
---
257+
233258
## License
234259

235260
This project is licensed under the Apache License, Version 2.0. See the [LICENSE](./LICENSE) file for details.

app.config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ConfigContext, ExpoConfig } from '@expo/config'
22

33
export default ({ config }: ConfigContext): ExpoConfig => {
44
const distribution = process.env.PEARPASS_DISTRIBUTION || 'standard'
5+
const isNightly = distribution === 'nightly'
56

67
const plugins = config.plugins ? [...config.plugins] : []
78
plugins.push(['./plugins/withAndroidDistribution', { distribution }])
@@ -14,6 +15,9 @@ export default ({ config }: ConfigContext): ExpoConfig => {
1415
extensionBundlePath: 'bundles/autofill.bundle',
1516
},
1617
])
18+
if (isNightly) {
19+
plugins.push('@sentry/react-native/expo')
20+
}
1721

1822
return {
1923
...config,
@@ -26,4 +30,3 @@ export default ({ config }: ConfigContext): ExpoConfig => {
2630
}
2731
}
2832
}
29-

index.js

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,27 @@
1-
import './strict.css';
2-
import '@expo/metro-runtime';
1+
import './strict.css'
2+
import '@expo/metro-runtime'
33

44
import { registerRootComponent } from 'expo'
55

6+
import { isNightly } from './src/constants/distribution'
67
import { Main } from './src/main'
8+
import { loadLogConfiguration } from './src/utils/logConfigurationStorage'
9+
import { logger } from './src/utils/logger'
710

8-
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
9-
// It also ensures that whether you load the app in Expo Go or in a native build,
10-
// the environment is set up appropriately
11+
// Should happen first - awaiting AsyncStorage before
12+
// registering races the native activity on Android — iOS happens to win
13+
// the race, Android does not, and the JS root never mounts.
1114
registerRootComponent(Main)
15+
16+
async function bootstrap() {
17+
await loadLogConfiguration()
18+
19+
if (isNightly()) {
20+
const { initSentry } = require('./src/utils/sentry')
21+
initSentry()
22+
} else {
23+
logger.log('[sentry] disabled — non-nightly distribution')
24+
}
25+
}
26+
27+
bootstrap()

jest.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export default {
1010
},
1111
testPathIgnorePatterns: ['/node_modules/', '/.yalc/', '/packages/'],
1212
transformIgnorePatterns: [
13-
'node_modules/(?!(react-native|@react-native|@react-native-community|react-redux|@reduxjs/toolkit|immer|styled-components|@testing-library/react-native|expo-local-authentication|@tetherto|pearpass-lib-ui-react-native-components|pearpass-lib-ui-theme-provider|pearpass-lib-ui-theme-provider/native|pearpass-utils-password-check|@react-navigation/bottom-tabs|@gorhom/bottom-sheet|pearpass-utils-password-generator|expo-clipboard|expo-haptics|expo-document-picker|expo-file-system|expo-modules-core|wdk-react-native-passkey-internal|react-native-passkey|axios|react-native-config|pearpass-lib-data-export|pearpass-lib-constants|react-native-toast-message|react-native-reanimated|react-native-vision-camera|react-native-worklets-core|vision-camera-zxing)/)'
13+
'node_modules/(?!(react-native|@react-native|@react-native-community|react-redux|@reduxjs/toolkit|immer|styled-components|@testing-library/react-native|expo|expo-local-authentication|@tetherto|pearpass-lib-ui-react-native-components|pearpass-lib-ui-theme-provider|pearpass-lib-ui-theme-provider/native|pearpass-utils-password-check|@react-navigation/bottom-tabs|@gorhom/bottom-sheet|pearpass-utils-password-generator|expo-clipboard|expo-constants|expo-haptics|expo-document-picker|expo-file-system|expo-modules-core|expo-sharing|wdk-react-native-passkey-internal|react-native-passkey|axios|react-native-config|pearpass-lib-data-export|pearpass-lib-constants|react-native-toast-message|react-native-reanimated|react-native-vision-camera|react-native-worklets-core|vision-camera-zxing)/)'
1414
],
1515
setupFilesAfterEnv: ['./jest.setup.js']
1616
}

jest.setup.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,39 @@ jest.mock('./src/context/HapticsContext', () => ({
5454
}),
5555
HapticsProvider: ({ children }) => children
5656
}))
57+
58+
jest.mock('@react-native-async-storage/async-storage', () =>
59+
require('@react-native-async-storage/async-storage/jest/async-storage-mock')
60+
)
61+
62+
jest.mock('expo-file-system/next', () => ({
63+
File: jest.fn().mockImplementation(() => ({
64+
exists: false,
65+
size: 0,
66+
create: jest.fn(),
67+
delete: jest.fn(),
68+
write: jest.fn(),
69+
textSync: jest.fn(() => ''),
70+
open: jest.fn(() => ({
71+
offset: 0,
72+
size: 0,
73+
writeBytes: jest.fn(),
74+
close: jest.fn()
75+
}))
76+
})),
77+
Paths: { cache: { uri: 'file:///mock-cache/' } }
78+
}))
79+
80+
jest.mock('expo-sharing', () => ({
81+
shareAsync: jest.fn(() => Promise.resolve())
82+
}))
83+
84+
jest.mock('expo-constants', () => ({
85+
__esModule: true,
86+
default: { expoConfig: { extra: {} } }
87+
}))
88+
89+
jest.mock('@sentry/react-native', () => ({
90+
init: jest.fn(),
91+
reactNavigationIntegration: jest.fn(() => ({}))
92+
}))

metro.config.cjs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
// Learn more https://docs.expo.io/guides/customizing-metro
2-
const { getDefaultConfig } = require('expo/metro-config')
2+
const isNightly = process.env.PEARPASS_DISTRIBUTION === 'nightly'
33

44
/** @type {import('expo/metro-config').MetroConfig} */
5-
const config = getDefaultConfig(__dirname)
5+
const config = isNightly
6+
? require('@sentry/react-native/metro').getSentryExpoConfig(__dirname, {
7+
includeWebFeedback: false
8+
})
9+
: require('expo/metro-config').getDefaultConfig(__dirname)
610

711
config.transformer = {
812
...config.transformer,

0 commit comments

Comments
 (0)