Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: run ubuntu-bug when error page is shown #770

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/ubuntu_bootstrap/lib/installer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,13 @@ Future<void> runInstallerApp(
tryRegisterService<AccessibilityService>(GnomeAccessibilityService.new);
tryRegisterService<ActiveDirectoryService>(
() => SubiquityActiveDirectoryService(getService<SubiquityClient>()));
tryRegisterService<ApportService>(() => ApportService(
liveRun: liveRun,
preCommands: [
// Make sure the log files are readable by the live session user.
(cmd: 'sudo', args: ['sh', '-c', 'chmod a+r /var/log/installer/*']),
],
));
tryRegisterServiceInstance<ArgResults>(options);
tryRegisterService<ConfigService>(ConfigService.new);
if (liveRun) tryRegisterService<DesktopService>(GnomeService.new);
Expand Down
1 change: 1 addition & 0 deletions packages/ubuntu_provision/lib/services.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export 'src/services/accessibility_service.dart';
export 'src/services/active_directory_service.dart';
export 'src/services/apport_service.dart';
export 'src/services/config_service.dart';
export 'src/services/desktop_service.dart';
export 'src/services/flavor_service.dart';
Expand Down
2 changes: 2 additions & 0 deletions packages/ubuntu_provision/lib/src/error/error_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ final logStreamProvider = Provider<Stream<String>>((ref) {
return logController.stream;
});

final apportProvider = Provider((ref) => tryGetService<ApportService>());

@immutable
class ErrorModel {
const ErrorModel({
Expand Down
20 changes: 16 additions & 4 deletions packages/ubuntu_provision/lib/src/error/error_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,30 @@ import 'package:yaru/widgets.dart';

/// This is an error page that is shown when an unexpected error occurs.
/// It shows a message and a the log.
class ErrorPage extends ConsumerWidget with ProvisioningPage {
class ErrorPage extends ConsumerStatefulWidget with ProvisioningPage {
const ErrorPage({
required this.allowRestart,
super.key,
});

final bool allowRestart;

@override
ConsumerState<ErrorPage> createState() => _ErrorPageState();
}

class _ErrorPageState extends ConsumerState<ErrorPage> {
final launchpadUrl =
'https://bugs.launchpad.net/ubuntu-desktop-provision/+filebug';

@override
Widget build(BuildContext context, WidgetRef ref) {
void initState() {
super.initState();
ref.read(apportProvider)?.launch();
}

@override
Widget build(BuildContext context) {
final name = ModalRoute.of(context)!.settings.name!.replaceFirst('/', '');
final image = ref.watch(pageImagesProvider).get(name);
final lang = UbuntuProvisionLocalizations.of(context);
Expand Down Expand Up @@ -118,11 +130,11 @@ class ErrorPage extends ConsumerWidget with ProvisioningPage {
),
const SizedBox(width: kWizardBarSpacing),
WizardButton(
label: allowRestart ? lang.restart : lang.close,
label: widget.allowRestart ? lang.restart : lang.close,
highlighted: true,
onActivated: () async {
final window = YaruWindow.of(context);
if (allowRestart) {
if (widget.allowRestart) {
await model.reboot().then((_) => window.close());
} else {
await window.close();
Expand Down
1 change: 1 addition & 0 deletions packages/ubuntu_provision/lib/src/locale/locale_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class LocalePage extends ConsumerWidget with ProvisioningPage {
Future<bool> load(BuildContext context, WidgetRef ref) async {
final model = ref.read(localeModelProvider);
await model.init();
throw Exception('Test Error');
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For testing purposes this exception is thrown when the language selection page is loaded.

unawaited(model.playWelcomeSound());
return true;
}
Expand Down
57 changes: 57 additions & 0 deletions packages/ubuntu_provision/lib/src/services/apport_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import 'dart:io';

import 'package:meta/meta.dart';
import 'package:ubuntu_logger/ubuntu_logger.dart';

final _log = Logger('apport_service');

class ApportService {
ApportService({
this.liveRun = true,
this.preCommands,
@visibleForTesting
Future<ProcessResult> Function(String, List<String>)? runProcess,
@visibleForTesting Map<String, String>? env,
}) : runProcess = runProcess ?? Process.run,
env = env ?? Platform.environment;

static const String apport = 'ubuntu-bug';

final bool liveRun;
final Future<ProcessResult> Function(String, List<String>) runProcess;
final Map<String, String> env;
final List<({String cmd, List<String> args})>? preCommands;

Future<void> launch() async {
final snapName = env['SNAP_NAME'];
if (snapName == null) {
_log.error('SNAP_NAME environment variable not set');
return;
}
if (liveRun) {
if (preCommands != null) {
for (final command in preCommands!) {
_log.info(
'Running pre-command ${command.cmd} ${command.args.join(' ')}');
final result = await runProcess(command.cmd, command.args);
if (result.exitCode != 0) {
_log.error('Failed to run ${command.cmd}: ${result.stderr}');
}
}
}
_log.info('Running apport for $snapName');
final result = await runProcess(apport, [snapName]);
if (result.exitCode != 0) {
_log.error('Failed to run apport for $snapName: ${result.stderr}');
}
} else {
if (preCommands != null) {
for (final command in preCommands!) {
_log.info(
'Dry-run: Running pre-command ${command.cmd} ${command.args.join(' ')}');
}
}
_log.info('Dry-run: Running apport for $snapName');
}
}
}
48 changes: 48 additions & 0 deletions packages/ubuntu_provision/test/services/apport_service_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'dart:io';

import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
import 'package:ubuntu_provision/ubuntu_provision.dart';

void main() {
test('run ubuntu-bug', () async {
final mockProcess = MockProcess();
when(mockProcess.run(any, any))
.thenAnswer((_) async => ProcessResult(0, 0, '', ''));
final mockEnv = <String, String>{'SNAP_NAME': 'snap-name'};
final service = ApportService(runProcess: mockProcess.run, env: mockEnv);

await service.launch();
verify(mockProcess.run('ubuntu-bug', ['snap-name'])).called(1);
});

test('pre commands', () async {
final mockProcess = MockProcess();
when(mockProcess.run(any, any))
.thenAnswer((_) async => ProcessResult(0, 0, '', ''));
final mockEnv = <String, String>{'SNAP_NAME': 'snap-name'};
final service = ApportService(
runProcess: mockProcess.run,
env: mockEnv,
preCommands: [
(cmd: 'echo', args: ['foo']),
],
);

await service.launch();
verify(mockProcess.run('echo', ['foo'])).called(1);
});
}

abstract class _Process {
Future<ProcessResult> run(String? executable, List<String>? arguments);
}

class MockProcess extends Mock implements _Process {
@override
Future<ProcessResult> run(String? executable, List<String>? arguments) =>
super.noSuchMethod(
Invocation.method(#run, [executable, arguments]),
returnValue: Future.value(ProcessResult(0, 0, '', '')),
) as Future<ProcessResult>;
}
Loading