felangel / mason

Tools which allow developers to create and consume reusable templates called bricks.
https://docs.brickhub.dev
935 stars 93 forks source link

fix: capture ProcessException when hook generation fails due to `dart pub get` #1242

Open alestiago opened 5 months ago

alestiago commented 5 months ago

Description

Running hooks using the Mason API throws a ProcessException if Dart executable is not installed.

Steps To Reproduce

  1. Run a Mason Generator:

    final generator = await MasonGenerator.fromBundle(myBundle);
    await generator.hooks.preGen(vars: {'name':'Dash'});
  2. See error:

    ProcessException: No such file or directory
    Command: /bin/sh -c 'dart' 'pub' 'get'

Expected Behavior

I would expect the error to be captured, it is currently not being captured.

I would expect a better error message that outlined the issue comes from Mason itself (since it is running the dart pub get internally) with a verbose description on how to resolve.

Additional Context

felangel commented 2 weeks ago

Hi @alestiago 👋 Can you elaborate? It looks like the error is being captured in https://github.com/felangel/mason/blob/8bf695d1dee6ca3dc1a44241dcec00ee3603653d/packages/mason/lib/src/hooks.dart#L222.

Can you share an example of what you'd expect the output to be? Thanks!

alestiago commented 2 weeks ago

Hi @felangel , this is quite old already and I don't have all the context I had back then. In a nutshell, the error was never a HookDependencyInstallFailure and it was reached in a CI environment, where if you tried running a Dart program with the sample code given it would fail with the ProcessException if Dart was configured in a particular way.


It looks like the error is being captured in line 222

I did notice the HookDependencyInstallFailure, but the reported error was never of such type, but a ProcessException instead. Might well be because the error code condition was not reached due to the thrown ProcessException.

Consider:

import 'dart:io';

Future<void> main() async {
  final result = await Process.run('not-a-command', ['pub', 'get']);

  if (result.exitCode != 0) {
    throw Exception('Something went wrong!);
  }
}

Running the above throws "ProcessException: No such file or directory" not the exception. Whereas:

import 'dart:io';

Future<void> main() async {
  try {
    await Process.run('not-a-command', ['pub', 'get']);
    // ...
  } catch (e) {
    throw Exception('Something went wrong!');
  }
}

Does capture the ProcessExcpetion and throws an Exception instead.


Can you share an example of what you'd expect the output to be?

I would expect the output to be an error message with a verbose explanation that includes information about what the program was trying to do, and a recommendation to solve the failure. So for example, something along the lines:

  try {
    await Process.run('not-a-command', ['pub', 'get']);
    // ...
  } catch (e) {
    throw HookDependencyInstallFailure('''
      Failed to install hook dependencies in $workingDirectory. 
      Make sure you have the Dart SDK installed and that the 'pub' command is available in your PATH.
     ''');
  }