mirror of
https://github.com/flutter/flutter
synced 2024-08-27 12:00:58 +00:00
Migrate crash_reporting and github_template (#91516)
This commit is contained in:
parent
98e66fdb6c
commit
6987c9fb82
|
@ -8,11 +8,9 @@ import 'base/context.dart';
|
|||
import 'doctor.dart';
|
||||
import 'ios/simulators.dart';
|
||||
import 'macos/xcdevice.dart';
|
||||
import 'reporting/crash_reporting.dart';
|
||||
|
||||
export 'globals_null_migrated.dart';
|
||||
|
||||
CrashReporter get crashReporter => context.get<CrashReporter>();
|
||||
Doctor get doctor => context.get<Doctor>();
|
||||
IOSSimulatorUtils get iosSimulatorUtils => context.get<IOSSimulatorUtils>();
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ import 'macos/cocoapods_validator.dart';
|
|||
import 'macos/xcode.dart';
|
||||
import 'persistent_tool_state.dart';
|
||||
import 'project.dart';
|
||||
import 'reporting/crash_reporting.dart';
|
||||
import 'reporting/reporting.dart';
|
||||
import 'runner/local_engine.dart';
|
||||
import 'version.dart';
|
||||
|
@ -49,6 +50,7 @@ BuildSystem? get buildSystem => context.get<BuildSystem>();
|
|||
Cache get cache => context.get<Cache>()!;
|
||||
CocoaPodsValidator? get cocoapodsValidator => context.get<CocoaPodsValidator>();
|
||||
Config get config => context.get<Config>()!;
|
||||
CrashReporter? get crashReporter => context.get<CrashReporter>();
|
||||
DeviceManager? get deviceManager => context.get<DeviceManager>();
|
||||
HttpClientFactory? get httpClientFactory => context.get<HttpClientFactory>();
|
||||
Logger get logger => context.get<Logger>()!;
|
||||
|
|
|
@ -2,13 +2,10 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.8
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:file/file.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
|
@ -43,14 +40,14 @@ const String _kStackTraceFilename = 'stacktrace_file';
|
|||
|
||||
class CrashDetails {
|
||||
CrashDetails({
|
||||
@required this.command,
|
||||
@required this.error,
|
||||
@required this.stackTrace,
|
||||
@required this.doctorText,
|
||||
required this.command,
|
||||
required this.error,
|
||||
required this.stackTrace,
|
||||
required this.doctorText,
|
||||
});
|
||||
|
||||
final String command;
|
||||
final dynamic error;
|
||||
final Object error;
|
||||
final StackTrace stackTrace;
|
||||
final String doctorText;
|
||||
}
|
||||
|
@ -58,10 +55,10 @@ class CrashDetails {
|
|||
/// Reports information about the crash to the user.
|
||||
class CrashReporter {
|
||||
CrashReporter({
|
||||
@required FileSystem fileSystem,
|
||||
@required Logger logger,
|
||||
@required FlutterProjectFactory flutterProjectFactory,
|
||||
@required HttpClient client,
|
||||
required FileSystem fileSystem,
|
||||
required Logger logger,
|
||||
required FlutterProjectFactory flutterProjectFactory,
|
||||
required HttpClient client,
|
||||
}) : _fileSystem = fileSystem,
|
||||
_logger = logger,
|
||||
_flutterProjectFactory = flutterProjectFactory,
|
||||
|
@ -110,11 +107,11 @@ class CrashReporter {
|
|||
/// wish to use your own server for collecting crash reports from Flutter Tools.
|
||||
class CrashReportSender {
|
||||
CrashReportSender({
|
||||
http.Client client,
|
||||
@required Usage usage,
|
||||
@required Platform platform,
|
||||
@required Logger logger,
|
||||
@required OperatingSystemUtils operatingSystemUtils,
|
||||
http.Client? client,
|
||||
required Usage usage,
|
||||
required Platform platform,
|
||||
required Logger logger,
|
||||
required OperatingSystemUtils operatingSystemUtils,
|
||||
}) : _client = client ?? http.Client(),
|
||||
_usage = usage,
|
||||
_platform = platform,
|
||||
|
@ -130,7 +127,7 @@ class CrashReportSender {
|
|||
bool _crashReportSent = false;
|
||||
|
||||
Uri get _baseUrl {
|
||||
final String overrideUrl = _platform.environment['FLUTTER_CRASH_SERVER_BASE_URL'];
|
||||
final String? overrideUrl = _platform.environment['FLUTTER_CRASH_SERVER_BASE_URL'];
|
||||
|
||||
if (overrideUrl != null) {
|
||||
return Uri.parse(overrideUrl);
|
||||
|
@ -147,10 +144,10 @@ class CrashReportSender {
|
|||
///
|
||||
/// The report is populated from data in [error] and [stackTrace].
|
||||
Future<void> sendReport({
|
||||
@required dynamic error,
|
||||
@required StackTrace stackTrace,
|
||||
@required String Function() getFlutterVersion,
|
||||
@required String command,
|
||||
required Object error,
|
||||
required StackTrace stackTrace,
|
||||
required String Function() getFlutterVersion,
|
||||
required String command,
|
||||
}) async {
|
||||
// Only send one crash report per run.
|
||||
if (_crashReportSent) {
|
||||
|
|
|
@ -2,13 +2,10 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.8
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:file/file.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
|
@ -25,10 +22,10 @@ import '../version.dart';
|
|||
/// Provide suggested GitHub issue templates to user when Flutter encounters an error.
|
||||
class GitHubTemplateCreator {
|
||||
GitHubTemplateCreator({
|
||||
@required FileSystem fileSystem,
|
||||
@required Logger logger,
|
||||
@required FlutterProjectFactory flutterProjectFactory,
|
||||
@required HttpClient client,
|
||||
required FileSystem fileSystem,
|
||||
required Logger logger,
|
||||
required FlutterProjectFactory flutterProjectFactory,
|
||||
required HttpClient client,
|
||||
}) : _fileSystem = fileSystem,
|
||||
_logger = logger,
|
||||
_flutterProjectFactory = flutterProjectFactory,
|
||||
|
@ -44,7 +41,7 @@ class GitHubTemplateCreator {
|
|||
}
|
||||
|
||||
/// Restricts exception object strings to contain only information about tool internals.
|
||||
static String sanitizedCrashException(dynamic error) {
|
||||
static String sanitizedCrashException(Object error) {
|
||||
if (error is ProcessException) {
|
||||
// Suppress args.
|
||||
return 'ProcessException: ${error.message} Command: ${error.executable}, OS error code: ${error.errorCode}';
|
||||
|
@ -83,7 +80,7 @@ class GitHubTemplateCreator {
|
|||
/// Shorten the URL, if possible.
|
||||
Future<String> toolCrashIssueTemplateGitHubURL(
|
||||
String command,
|
||||
dynamic error,
|
||||
Object error,
|
||||
StackTrace stackTrace,
|
||||
String doctorText
|
||||
) async {
|
||||
|
@ -131,13 +128,14 @@ ${_projectMetadataInformation()}
|
|||
return exception.toString();
|
||||
}
|
||||
try {
|
||||
final FlutterManifest manifest = project?.manifest;
|
||||
final FlutterManifest manifest = project.manifest;
|
||||
if (project == null || manifest == null || manifest.isEmpty) {
|
||||
return 'No pubspec in working directory.';
|
||||
}
|
||||
final FlutterProjectMetadata metadata = FlutterProjectMetadata(project.metadataFile, _logger);
|
||||
final FlutterProjectType? projectType = metadata.projectType;
|
||||
final StringBuffer description = StringBuffer()
|
||||
..writeln('**Type**: ${flutterProjectTypeToString(metadata.projectType)}')
|
||||
..writeln('**Type**: ${projectType == null ? 'malformed' : flutterProjectTypeToString(projectType)}')
|
||||
..writeln('**Version**: ${manifest.appVersion}')
|
||||
..writeln('**Material**: ${manifest.usesMaterialDesign}')
|
||||
..writeln('**Android X**: ${manifest.usesAndroidX}')
|
||||
|
@ -175,7 +173,7 @@ ${_projectMetadataInformation()}
|
|||
///
|
||||
/// See https://github.blog/2011-11-10-git-io-github-url-shortener.
|
||||
Future<String> _shortURL(String fullURL) async {
|
||||
String url;
|
||||
String? url;
|
||||
try {
|
||||
_logger.printTrace('Attempting git.io shortener: $fullURL');
|
||||
final List<int> bodyBytes = utf8.encode('url=${Uri.encodeQueryComponent(fullURL)}');
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// @dart = 2.8
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:file/file.dart';
|
||||
|
@ -12,8 +10,6 @@ import 'package:flutter_tools/src/base/io.dart';
|
|||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/os.dart';
|
||||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/doctor.dart';
|
||||
import 'package:flutter_tools/src/doctor_validator.dart';
|
||||
import 'package:flutter_tools/src/project.dart';
|
||||
import 'package:flutter_tools/src/reporting/crash_reporting.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
|
@ -25,11 +21,12 @@ import '../src/fake_http_client.dart';
|
|||
import '../src/fake_process_manager.dart';
|
||||
|
||||
void main() {
|
||||
BufferLogger logger;
|
||||
FileSystem fs;
|
||||
TestUsage testUsage;
|
||||
Platform platform;
|
||||
OperatingSystemUtils operatingSystemUtils;
|
||||
late BufferLogger logger;
|
||||
late FileSystem fs;
|
||||
late TestUsage testUsage;
|
||||
late Platform platform;
|
||||
late OperatingSystemUtils operatingSystemUtils;
|
||||
late StackTrace stackTrace;
|
||||
|
||||
setUp(() async {
|
||||
logger = BufferLogger.test();
|
||||
|
@ -45,6 +42,9 @@ void main() {
|
|||
);
|
||||
|
||||
MockCrashReportSender.sendCalls = 0;
|
||||
stackTrace = StackTrace.fromString('''
|
||||
#0 _File.open.<anonymous closure> (dart:io/file_impl.dart:366:9)
|
||||
#1 _rootRunUnary (dart:async/zone.dart:1141:38)''');
|
||||
});
|
||||
|
||||
Future<void> verifyCrashReportSent(RequestInfo crashInfo, {
|
||||
|
@ -62,15 +62,15 @@ void main() {
|
|||
'version': 'test-version',
|
||||
},
|
||||
));
|
||||
expect(crashInfo.fields['uuid'], testUsage.clientId);
|
||||
expect(crashInfo.fields['product'], 'Flutter_Tools');
|
||||
expect(crashInfo.fields['version'], 'test-version');
|
||||
expect(crashInfo.fields['osName'], 'linux');
|
||||
expect(crashInfo.fields['osVersion'], 'Linux');
|
||||
expect(crashInfo.fields['type'], 'DartError');
|
||||
expect(crashInfo.fields['error_runtime_type'], 'StateError');
|
||||
expect(crashInfo.fields['error_message'], 'Bad state: Test bad state error');
|
||||
expect(crashInfo.fields['comments'], 'crash');
|
||||
expect(crashInfo.fields?['uuid'], testUsage.clientId);
|
||||
expect(crashInfo.fields?['product'], 'Flutter_Tools');
|
||||
expect(crashInfo.fields?['version'], 'test-version');
|
||||
expect(crashInfo.fields?['osName'], 'linux');
|
||||
expect(crashInfo.fields?['osVersion'], 'Linux');
|
||||
expect(crashInfo.fields?['type'], 'DartError');
|
||||
expect(crashInfo.fields?['error_runtime_type'], 'StateError');
|
||||
expect(crashInfo.fields?['error_message'], 'Bad state: Test bad state error');
|
||||
expect(crashInfo.fields?['comments'], 'crash');
|
||||
|
||||
expect(logger.traceText, contains('Sending crash report to Google.'));
|
||||
expect(logger.traceText, contains('Crash report sent (report ID: test-report-id)'));
|
||||
|
@ -112,7 +112,7 @@ void main() {
|
|||
|
||||
await crashReportSender.sendReport(
|
||||
error: StateError('Test bad state error'),
|
||||
stackTrace: null,
|
||||
stackTrace: stackTrace,
|
||||
getFlutterVersion: () => 'test-version',
|
||||
command: 'crash',
|
||||
);
|
||||
|
@ -138,7 +138,7 @@ void main() {
|
|||
|
||||
await crashReportSender.sendReport(
|
||||
error: StateError('Test bad state error'),
|
||||
stackTrace: null,
|
||||
stackTrace: stackTrace,
|
||||
getFlutterVersion: () => 'test-version',
|
||||
command: 'crash',
|
||||
);
|
||||
|
@ -157,7 +157,7 @@ void main() {
|
|||
|
||||
await crashReportSender.sendReport(
|
||||
error: StateError('Test bad state error'),
|
||||
stackTrace: null,
|
||||
stackTrace: stackTrace,
|
||||
getFlutterVersion: () => 'test-version',
|
||||
command: 'crash',
|
||||
);
|
||||
|
@ -176,7 +176,7 @@ void main() {
|
|||
|
||||
await crashReportSender.sendReport(
|
||||
error: StateError('Test bad state error'),
|
||||
stackTrace: null,
|
||||
stackTrace: stackTrace,
|
||||
getFlutterVersion: () => 'test-version',
|
||||
command: 'crash',
|
||||
);
|
||||
|
@ -195,7 +195,7 @@ void main() {
|
|||
|
||||
await crashReportSender.sendReport(
|
||||
error: ClientException('Test bad state error'),
|
||||
stackTrace: null,
|
||||
stackTrace: stackTrace,
|
||||
getFlutterVersion: () => 'test-version',
|
||||
command: 'crash',
|
||||
);
|
||||
|
@ -216,28 +216,28 @@ void main() {
|
|||
|
||||
await crashReportSender.sendReport(
|
||||
error: StateError('Test bad state error'),
|
||||
stackTrace: null,
|
||||
stackTrace: stackTrace,
|
||||
getFlutterVersion: () => 'test-version',
|
||||
command: 'crash',
|
||||
);
|
||||
|
||||
await crashReportSender.sendReport(
|
||||
error: StateError('Test bad state error'),
|
||||
stackTrace: null,
|
||||
stackTrace: stackTrace,
|
||||
getFlutterVersion: () => 'test-version',
|
||||
command: 'crash',
|
||||
);
|
||||
|
||||
await crashReportSender.sendReport(
|
||||
error: StateError('Test bad state error'),
|
||||
stackTrace: null,
|
||||
stackTrace: stackTrace,
|
||||
getFlutterVersion: () => 'test-version',
|
||||
command: 'crash',
|
||||
);
|
||||
|
||||
await crashReportSender.sendReport(
|
||||
error: StateError('Test bad state error'),
|
||||
stackTrace: null,
|
||||
stackTrace: stackTrace,
|
||||
getFlutterVersion: () => 'test-version',
|
||||
command: 'crash',
|
||||
);
|
||||
|
@ -247,8 +247,8 @@ void main() {
|
|||
});
|
||||
|
||||
testWithoutContext('should not send a crash report if on a user-branch', () async {
|
||||
String method;
|
||||
Uri uri;
|
||||
String? method;
|
||||
Uri? uri;
|
||||
|
||||
final MockClient mockClient = MockClient((Request request) async {
|
||||
method = request.method;
|
||||
|
@ -270,7 +270,7 @@ void main() {
|
|||
|
||||
await crashReportSender.sendReport(
|
||||
error: StateError('Test bad state error'),
|
||||
stackTrace: null,
|
||||
stackTrace: stackTrace,
|
||||
getFlutterVersion: () => '[user-branch]/v1.2.3',
|
||||
command: 'crash',
|
||||
);
|
||||
|
@ -283,7 +283,7 @@ void main() {
|
|||
});
|
||||
|
||||
testWithoutContext('can override base URL', () async {
|
||||
Uri uri;
|
||||
Uri? uri;
|
||||
final MockClient mockClient = MockClient((Request request) async {
|
||||
uri = request.url;
|
||||
return Response('test-report-id', 200);
|
||||
|
@ -307,7 +307,7 @@ void main() {
|
|||
|
||||
await crashReportSender.sendReport(
|
||||
error: StateError('Test bad state error'),
|
||||
stackTrace: null,
|
||||
stackTrace: stackTrace,
|
||||
getFlutterVersion: () => 'test-version',
|
||||
command: 'crash',
|
||||
);
|
||||
|
@ -329,9 +329,9 @@ void main() {
|
|||
}
|
||||
|
||||
class RequestInfo {
|
||||
String method;
|
||||
Uri uri;
|
||||
Map<String, String> fields;
|
||||
String? method;
|
||||
Uri? uri;
|
||||
Map<String, String>? fields;
|
||||
}
|
||||
|
||||
class MockCrashReportSender extends MockClient {
|
||||
|
@ -341,21 +341,20 @@ class MockCrashReportSender extends MockClient {
|
|||
crashInfo.uri = request.url;
|
||||
|
||||
// A very ad-hoc multipart request parser. Good enough for this test.
|
||||
String boundary = request.headers['Content-Type'];
|
||||
boundary = boundary.substring(boundary.indexOf('boundary=') + 9);
|
||||
String? boundary = request.headers['Content-Type'];
|
||||
boundary = boundary?.substring(boundary.indexOf('boundary=') + 9);
|
||||
crashInfo.fields = Map<String, String>.fromIterable(
|
||||
utf8.decode(request.bodyBytes)
|
||||
.split('--$boundary')
|
||||
.map<List<String>>((String part) {
|
||||
final Match nameMatch = RegExp(r'name="(.*)"').firstMatch(part);
|
||||
.map<List<String>?>((String part) {
|
||||
final Match? nameMatch = RegExp(r'name="(.*)"').firstMatch(part);
|
||||
if (nameMatch == null) {
|
||||
return null;
|
||||
}
|
||||
final String name = nameMatch[1];
|
||||
final String name = nameMatch[1]!;
|
||||
final String value = part.split('\n').skip(2).join('\n').trim();
|
||||
return <String>[name, value];
|
||||
})
|
||||
.where((List<String> pair) => pair != null),
|
||||
}).whereType<List<String>>(),
|
||||
key: (dynamic key) {
|
||||
final List<String> pair = key as List<String>;
|
||||
return pair[0];
|
||||
|
@ -380,13 +379,3 @@ class CrashingCrashReportSender extends MockClient {
|
|||
throw exception;
|
||||
});
|
||||
}
|
||||
|
||||
/// A DoctorValidatorsProvider that overrides the default validators without
|
||||
/// overriding the doctor.
|
||||
class FakeDoctorValidatorsProvider implements DoctorValidatorsProvider {
|
||||
@override
|
||||
List<DoctorValidator> get validators => <DoctorValidator>[];
|
||||
|
||||
@override
|
||||
List<Workflow> get workflows => <Workflow>[];
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ import 'package:flutter_tools/src/base/io.dart' as io;
|
|||
import 'package:flutter_tools/src/base/platform.dart';
|
||||
import 'package:flutter_tools/src/base/user_messages.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals;
|
||||
import 'package:flutter_tools/src/globals_null_migrated.dart' as globals;
|
||||
import 'package:flutter_tools/src/reporting/crash_reporting.dart';
|
||||
import 'package:flutter_tools/src/reporting/reporting.dart';
|
||||
import 'package:flutter_tools/src/runner/flutter_command.dart';
|
||||
|
|
Loading…
Reference in a new issue