Compare commits

...
Author SHA1 Message Date
Thomas SharedInboxandClaude Sonnet 4.6 37abd5abb6 fix: add git hash to crash screen and extend DB path retries (#179)
Two issues from #179:
- crash_screen.dart now reads GIT_HASH compile-time constant and includes
  'Git Commit: <hash>' in both the on-screen UI and the copied report, so
  crash reports always show the exact build that crashed.
- _resolveDatabasePath() retry delays extended from [100, 300, 600] ms
  (total ~1 s, 4 attempts) to [200, 500, 1000, 2000, 4000] ms (total
  ~7.7 s, 6 attempts) to handle slow/non-standard Android devices where
  the path_provider Pigeon channel takes several seconds to become ready.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 16:05:05 +02:00
3 changed files with 59 additions and 2 deletions
+4 -2
View File
@@ -596,8 +596,10 @@ Future<void> initDatabasePath() async {
Future<String> _resolveDatabasePath() async {
if (_dbPath != null) return _dbPath!;
// initDatabasePath() failed (channel not ready before runApp). Retry now
// that the engine is fully initialised, with brief back-off.
const delays = [100, 300, 600];
// that the engine is fully initialised, with exponential back-off.
// Longer delays handle slow/non-standard Android devices where the Pigeon
// channel takes several seconds to become available.
const delays = [200, 500, 1000, 2000, 4000];
for (final ms in delays) {
try {
final dir = await getApplicationSupportDirectory();
+12
View File
@@ -15,15 +15,19 @@ class CrashScreen extends StatelessWidget {
final Object exception;
final StackTrace? stackTrace;
static const _gitHash = String.fromEnvironment('GIT_HASH');
Future<String> _buildReport() async {
String version = 'unknown';
try {
final info = await PackageInfo.fromPlatform();
version = '${info.version}+${info.buildNumber}';
} catch (_) {}
final gitLine = _gitHash.isNotEmpty ? 'Git Commit: $_gitHash\n' : '';
final platform =
'${Platform.operatingSystem} ${Platform.operatingSystemVersion}';
return 'App Version: $version\n'
'$gitLine'
'Platform: $platform\n\n'
'Error:\n```\n$exception\n```\n\n'
'Stack Trace:\n```\n$stackTrace\n```';
@@ -50,6 +54,14 @@ class CrashScreen extends StatelessWidget {
style: Theme.of(ctx).textTheme.titleMedium,
textAlign: TextAlign.center,
),
if (_gitHash.isNotEmpty) ...[
const SizedBox(height: 8),
const Text(
'Git Commit: $_gitHash',
style: TextStyle(fontSize: 12, color: Colors.grey),
textAlign: TextAlign.center,
),
],
const SizedBox(height: 24),
const Text(
'Error Details:',
+43
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:package_info_plus/package_info_plus.dart';
@@ -76,6 +77,48 @@ void main() {
expect(mock.launchedUrl, isNot(contains('Stack%20Trace')));
});
testWidgets(
'Copy to Clipboard includes App Version and excludes Git Commit when GIT_HASH is empty',
(
tester,
) async {
tester.view.physicalSize = const Size(800, 1200);
tester.view.devicePixelRatio = 1.0;
addTearDown(() => tester.view.resetPhysicalSize());
UrlLauncherPlatform.instance = MockUrlLauncher();
String? capturedClipboard;
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.platform,
(MethodCall call) async {
if (call.method == 'Clipboard.setData') {
capturedClipboard = (call.arguments as Map)['text'] as String;
}
return null;
},
);
const exception = 'TestException: clipboard test';
final stackTrace = StackTrace.current;
await tester.pumpWidget(
MaterialApp(
home: CrashScreen(exception: exception, stackTrace: stackTrace),
),
);
await tester.tap(find.text('Copy to Clipboard'));
await tester.pumpAndSettle();
expect(capturedClipboard, isNotNull);
expect(capturedClipboard, contains('App Version:'));
expect(capturedClipboard, contains('Platform:'));
expect(capturedClipboard, contains('TestException: clipboard test'));
// GIT_HASH is empty in tests (no --dart-define), so the line is omitted.
expect(capturedClipboard, isNot(contains('Git Commit:')));
});
testWidgets(
'CrashScreen used as root widget — buttons work without ScaffoldMessenger crash',
(tester) async {