Problem/Motivation

1) always catch \Throwable, not \Exception
2) always pass the old exception when re-throwing it. Otherwise, we lose the backtrace.

example --

   try {
      $ignored_paths = $this->getIgnoredPaths();
    }
    catch (\Exception $e) {
      throw new StageException($e->getMessage());
    }

… but we should really do this everywhere where Stage is currently catching exceptions.

Steps to reproduce

Proposed resolution

Remaining tasks

User interface changes

API changes

Data model changes

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

omkar.podey created an issue. See original summary.

omkar.podey’s picture

\Throwable over \Exception ?

I changed the code to catch \Throwable instead of \Exception (\Throwable covers both php errors and exceptions)

  1. First, I think we don't want our code execution to stop abruptly on an error and allow it to cleanup (like destroying the stage and other things) and catching errors using \Throwable allows us to do that.
  2. Another reason i cloud think of is if we catch the errors too then we can probably format and display them better which might make it easier to debug.

Is that what you were thinking @phenaproxima ? or am i missing something ?

omkar.podey’s picture

Assigned: omkar.podey » phenaproxima
Status: Active » Needs review
wim leers’s picture

For #3.1 to be actually true, we should apply this pattern far more widely than only when trying $ignored_paths = $this->getIgnoredPaths();.

Assigning to @phenaproxima for review on both #3 and the preceding paragraph.

wim leers’s picture

phenaproxima’s picture

First, I think we don't want our code execution to stop abruptly on an error and allow it to cleanup (like destroying the stage and other things) and catching errors using \Throwable allows us to do that.

This is what I'm thinking. I don't see any reason, generally, for us to treat errors and exceptions any differently in Package Manager and AU. (If there is such a case, it should be one-off and heavily commented.) To me, being resilient means we should catch and handle as many problems as we can, and that means catching \Throwable.

tedbow’s picture

Version: 8.x-2.x-dev » 3.0.x-dev
wim leers’s picture

Assigned: phenaproxima » Unassigned
Issue summary: View changes
Status: Needs review » Needs work

Okay, based on #7, @phenaproxima clearly agrees with what I wrote in #5.

However … it looks like that is already the case!

But always pass the old exception when re-throwing. is not yet consistently implemented by Stage. Marking Needs work for that.

wim leers’s picture

Issue tags: +core-mvp

Since this affects reliability, tagging core-mvp.

omkar.podey’s picture

Assigned: Unassigned » omkar.podey
omkar.podey’s picture

Assigned: omkar.podey » Unassigned
Status: Needs work » Needs review

I don't see any more instances to be changed, setting to needs review

wim leers’s picture

Assigned: Unassigned » omkar.podey
Status: Needs review » Needs work

The MR is still targeting 8.x-2.x 😅 We need a new MR, that targets 3.0.x.

wim leers’s picture

@omkar.podey: please also add diff /var/www/html/core/scripts/dev/commit-code-check.sh modules/contrib/automatic_updates/commit-code-check.sh like I suggested during the call — that's going to make comparing this simpler.

Also: ls -al /var/www/html/core and ls -al modules/contrib/automatic_updates.

All just prior to executing modules/contrib/automatic_updates/commit-code-check.sh --drupalci.

wim leers’s picture

This is an absolute nightmare to figure out. This approach is also a nightmare to debug. Enough. The alternative approach I've started at #3343430-8: Stop reusing core's commit-code-check.sh in favor of 4 simple commands looks promising.

For this issue: just reverting all code quality checks temporarily, so @omkar.podey can continue 👍

omkar.podey’s picture

Assigned: omkar.podey » wim leers
Status: Needs work » Needs review
wim leers’s picture

Assigned: wim leers » Unassigned
Status: Needs review » Reviewed & tested by the community

Looks good!

🚢

phenaproxima’s picture

Status: Reviewed & tested by the community » Needs review

Question.

Do we have any other places where we are directly re-throwing a caught exception? Something like:

try {
  // Do an exceptional thing
}
catch (\Throwable $e) {
  // Do stuff...
  throw $e;
}

...because, in those cases, we are also losing the backtrace of $e, and should be wrapping those exceptions too in most cases.

wim leers’s picture

Status: Needs review » Reviewed & tested by the community

Ideally we'd have test coverage of this. It wouldn't be too hard to add, but I'm not sure it would be worth it at this point.

This.

The point is that it's not knowable what might fail, but that we have a big net, catch it, and surface it in an appropriate way. That is already the case here! 👍

Do we have any other places where we are directly re-throwing a caught exception? Something like:

I specifically checked that. See #9.

(Method used: search for throw new in all *.php files in both src and package_manager/src.)

phenaproxima’s picture

Title: Always catch \Throwable, not \Exception and always pass the old exception when re-throwing. » Always catch \Throwable, not \Exception, and pass the old exception when re-throwing.
phenaproxima’s picture

Status: Reviewed & tested by the community » Needs review

(Method used: search for throw new in all *.php files in both src and package_manager/src.)

🤔 Well...throw new would not be the thing. It'd be more like throw $ (i.e., throwing some already-caught exception).

And grep -irn 'throw \$' . does find some stuff:

./package_manager/tests/modules/package_manager_test_validation/src/EventSubscriber/TestSubscriber.php:136:      throw $results;
./package_manager/tests/modules/package_manager_bypass/src/LoggingCommitter.php:49:      throw $exception;
./package_manager/tests/src/Kernel/ComposerExecutableValidatorTest.php:300:      throw $output;
./package_manager/tests/src/Kernel/FixtureManipulatorTest.php:390:        throw $exception;
./package_manager/src/ComposerInspector.php:106:          throw $e;
./package_manager/src/Stage.php:540:      throw $error;
./automatic_updates_extensions/src/BatchProcessor.php:67:    throw $error;
./src/Validator/VersionPolicyValidator.php:198:        throw $unknown_target;
./src/Validator/VersionPolicyValidator.php:211:    throw $unknown_target;
./src/BatchProcessor.php:77:    throw $error;

Most of these seem fine. The only one that's maybe a little questionable is the one in ComposerInspector; we're re-throwing an exception from Composer Stager. IMHO that one should be wrapped. Thoughts?

wim leers’s picture

Status: Needs review » Reviewed & tested by the community

Oh!

Interesting. But that's not the pattern this issue was aiming to address, that's why I didn't check for that.

No, the one in ComposerInspector is 100% intentional. (That happened in #3311229: Validate compliance with composer minimum stability during PreRequireEvent after very careful deliberation.)

We are not in the business of wrapping composer-stager's exceptions in ComposerInspector because validators ought to handle exceptions of composer-stager themselves.

Otherwise, \Drupal\package_manager\Validator\ComposerSettingsValidator::validate() wouldn't have to do this for example:

    try {
      $setting = (int) $this->inspector->getConfig('secure-http', $dir);
    }
    catch (\Exception $exception) {
      $event->addErrorFromThrowable($exception, $this->t('Unable to determine Composer <code>secure-http

setting.'));
return;
}

If you're arguing that ComposerInspector should wrap all \PhpTuf\ComposerStager\Domain\Service\ProcessRunner\ComposerRunnerInterface::run() exceptions, that'd be a different concern/discussion, and IMHO out of scope.

phenaproxima’s picture

Status: Reviewed & tested by the community » Fixed

You've convinced me. Merged!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.