diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml
index 107aa2530..a57c5a804 100644
--- a/.github/workflows/continuous-integration.yml
+++ b/.github/workflows/continuous-integration.yml
@@ -11,6 +11,7 @@ on:
env:
COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --prefer-dist"
COMPOSER_UPDATE_FLAGS: ""
+ COMPOSER_TESTS_ARE_RUNNING: "1"
SYMFONY_PHPUNIT_VERSION: "8.3"
SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT: "1"
diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php
index 622938476..acd0e4c0a 100644
--- a/src/Composer/Console/Application.php
+++ b/src/Composer/Console/Application.php
@@ -22,6 +22,7 @@ use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
+use Seld\JsonLint\ParsingException;
use Composer\Command;
use Composer\Composer;
use Composer\Factory;
@@ -191,6 +192,20 @@ class Application extends BaseApplication
}
} catch (NoSslException $e) {
// suppress these as they are not relevant at this point
+ } catch (ParsingException $e) {
+ $details = $e->getDetails();
+
+ $file = realpath(Factory::getComposerFile());
+
+ $line = null;
+ if ($details && isset($details['line'])) {
+ $line = $details['line'];
+ }
+
+ $ghe = new GithubActionError($this->io);
+ $ghe->emit($e->getMessage(), $file, $line);
+
+ throw $e;
}
$this->hasPluginCommands = true;
@@ -237,7 +252,7 @@ class Application extends BaseApplication
if (function_exists('posix_getuid') && posix_getuid() === 0) {
if ($commandName !== 'self-update' && $commandName !== 'selfupdate') {
$io->writeError('Do not run Composer as root/super user! See https://getcomposer.org/root for details');
-
+
if ($io->isInteractive()) {
if (!$io->askConfirmation('Continue as root/super user [yes]? ', true)) {
return 1;
@@ -307,8 +322,13 @@ class Application extends BaseApplication
} catch (ScriptExecutionException $e) {
return (int) $e->getCode();
} catch (\Exception $e) {
+ $ghe = new GithubActionError($this->io);
+ $ghe->emit($e->getMessage());
+
$this->hintCommonErrors($e);
+
restore_error_handler();
+
throw $e;
}
}
diff --git a/src/Composer/Console/GithubActionError.php b/src/Composer/Console/GithubActionError.php
new file mode 100644
index 000000000..bbf5eaca1
--- /dev/null
+++ b/src/Composer/Console/GithubActionError.php
@@ -0,0 +1,40 @@
+io = $io;
+ }
+
+ /**
+ * @param string $message
+ * @param null|string $file
+ * @param null|int $line
+ */
+ public function emit($message, $file = null, $line = null)
+ {
+ if (getenv('GITHUB_ACTIONS') && !getenv('COMPOSER_TESTS_ARE_RUNNING')) {
+ // newlines need to be encoded
+ // see https://github.com/actions/starter-workflows/issues/68#issuecomment-581479448
+ $message = str_replace("\n", '%0A', $message);
+
+ if ($file && $line) {
+ $this->io->write("::error file=". $file .",line=". $line ."::". $message);
+ } elseif ($file) {
+ $this->io->write("::error file=". $file ."::". $message);
+ } else {
+ $this->io->write("::error ::". $message);
+ }
+ }
+ }
+}
diff --git a/src/Composer/Installer.php b/src/Composer/Installer.php
index b98a88c12..f8c788e10 100644
--- a/src/Composer/Installer.php
+++ b/src/Composer/Installer.php
@@ -13,6 +13,7 @@
namespace Composer;
use Composer\Autoload\AutoloadGenerator;
+use Composer\Console\GithubActionError;
use Composer\DependencyResolver\DefaultPolicy;
use Composer\DependencyResolver\LocalRepoTransaction;
use Composer\DependencyResolver\LockTransaction;
@@ -412,12 +413,18 @@ class Installer
$ruleSetSize = $solver->getRuleSetSize();
$solver = null;
} catch (SolverProblemsException $e) {
- $this->io->writeError('Your requirements could not be resolved to an installable set of packages.', true, IOInterface::QUIET);
- $this->io->writeError($e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose()));
+ $err = 'Your requirements could not be resolved to an installable set of packages.';
+ $prettyProblem = $e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose());
+
+ $this->io->writeError(''. $err .'', true, IOInterface::QUIET);
+ $this->io->writeError($prettyProblem);
if (!$this->devMode) {
$this->io->writeError('Running update with --no-dev does not mean require-dev is ignored, it just means the packages will not be installed. If dev requirements are blocking the update you have to resolve those problems.', true, IOInterface::QUIET);
}
+ $ghe = new GithubActionError($this->io);
+ $ghe->emit($err."\n".$prettyProblem);
+
return max(1, $e->getCode());
}
@@ -571,10 +578,16 @@ class Installer
$nonDevLockTransaction = $solver->solve($request, $this->ignorePlatformReqs);
$solver = null;
} catch (SolverProblemsException $e) {
- $this->io->writeError('Unable to find a compatible set of packages based on your non-dev requirements alone.', true, IOInterface::QUIET);
+ $err = 'Unable to find a compatible set of packages based on your non-dev requirements alone.';
+ $prettyProblem = $e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose(), true);
+
+ $this->io->writeError(''. $err .'', true, IOInterface::QUIET);
$this->io->writeError('Your requirements can be resolved successfully when require-dev packages are present.');
$this->io->writeError('You may need to move packages from require-dev or some of their dependencies to require.');
- $this->io->writeError($e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose(), true));
+ $this->io->writeError($prettyProblem);
+
+ $ghe = new GithubActionError($this->io);
+ $ghe->emit($err."\n".$prettyProblem);
return max(1, $e->getCode());
}
@@ -637,8 +650,14 @@ class Installer
return 1;
}
} catch (SolverProblemsException $e) {
- $this->io->writeError('Your lock file does not contain a compatible set of packages. Please run composer update.', true, IOInterface::QUIET);
- $this->io->writeError($e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose()));
+ $err = 'Your lock file does not contain a compatible set of packages. Please run composer update.';
+ $prettyProblem = $e->getPrettyString($repositorySet, $request, $pool, $this->io->isVerbose());
+
+ $this->io->writeError(''. $err .'', true, IOInterface::QUIET);
+ $this->io->writeError($prettyProblem);
+
+ $ghe = new GithubActionError($this->io);
+ $ghe->emit($err."\n".$prettyProblem);
return max(1, $e->getCode());
}