Skip to content

Commit 49934ff

Browse files
authored
Add forward mode (#67)
1 parent f7fb03e commit 49934ff

File tree

4 files changed

+148
-10
lines changed

4 files changed

+148
-10
lines changed

README.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
1. [Auto-installation](#auto-installation)
1919
1. [Disable links](#disable-links)
2020
1. [Change directory](#change-directory)
21+
1. [Forward mode](#forward-mode)
2122
1. [Reduce clutter](#reduce-clutter)
2223
1. [Related plugins](#related-plugins)
2324

@@ -162,7 +163,6 @@ For convenience, you can add the following script in your `composer.json` :
162163

163164
This makes sure all your bins are installed during `composer install` and updated during `composer update`.
164165

165-
166166
### Disable links
167167

168168
By default, binaries of the sub namespaces are linked to the root one like described in [example](#example). If you
@@ -178,7 +178,6 @@ wish to disable that behaviour, you can do so by adding a little setting in the
178178
}
179179
```
180180

181-
182181
### Change directory
183182

184183
By default, the packages are looked for in the `vendor-bin` directory. The location can be changed using `target-directory` value in the extra config:
@@ -193,6 +192,23 @@ By default, the packages are looked for in the `vendor-bin` directory. The locat
193192
}
194193
```
195194

195+
### Forward mode
196+
197+
There is a `forward mode` which is disabled by default. This can be activated by using the `forward-command` value in the extra config.
198+
199+
```json
200+
{
201+
"extra": {
202+
"bamarni-bin": {
203+
"forward-command": true
204+
}
205+
}
206+
}
207+
```
208+
209+
If this mode is activated, all your `composer install` and `composer update` commands are forwared to all bin directories.
210+
This is an replacement for the tasks shown in section [Auto-installation](#auto-installation).
211+
196212
### Reduce clutter
197213

198214
You can add following line to your `.gitignore` file in order to avoid committing dependencies of your tools.

src/BinCommand.php

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public function execute(InputInterface $input, OutputInterface $output)
4545

4646
$vendorRoot = $config->getTargetDirectory();
4747
$namespace = $input->getArgument('namespace');
48+
4849
$input = new StringInput(preg_replace(
4950
sprintf('/bin\s+(--ansi\s)?%s(\s.+)/', preg_quote($namespace, '/')),
5051
'$1$2',
@@ -77,8 +78,12 @@ private function executeAllNamespaces(ComposerApplication $application, $binVend
7778

7879
$originalWorkingDir = getcwd();
7980
$exitCode = 0;
80-
foreach ($binRoots as $binRoot) {
81-
$exitCode += $this->executeInNamespace($application, $binRoot, $input, $output);
81+
foreach ($binRoots as $namespace) {
82+
$output->writeln(
83+
sprintf('Run in namespace <comment>%s</comment>', $namespace),
84+
OutputInterface::VERBOSITY_VERBOSE
85+
);
86+
$exitCode += $this->executeInNamespace($application, $namespace, $input, $output);
8287

8388
chdir($originalWorkingDir);
8489
$this->resetComposers($application);
@@ -107,11 +112,15 @@ private function executeInNamespace(ComposerApplication $application, $namespace
107112
if (!file_exists(Factory::getComposerFile())) {
108113
file_put_contents(Factory::getComposerFile(), '{}');
109114
}
110-
115+
111116
$input = new StringInput((string) $input . ' --working-dir=.');
112117

113-
$this->getIO()->writeError('<info>Run with <comment>' . $input->__toString() . '</comment></info>', true, IOInterface::VERBOSE);
114-
118+
$this->getIO()->writeError(
119+
sprintf('<info>Run with <comment>%s</comment></info>', $input->__toString()),
120+
true,
121+
IOInterface::VERBOSE
122+
);
123+
115124
return $application->doRun($input, $output);
116125
}
117126

@@ -127,23 +136,39 @@ public function isProxyCommand()
127136
* Resets all Composer references in the application.
128137
*
129138
* @param ComposerApplication $application
139+
* @return void
130140
*/
131141
private function resetComposers(ComposerApplication $application)
132142
{
133143
$application->resetComposer();
144+
134145
foreach ($this->getApplication()->all() as $command) {
135146
if ($command instanceof BaseCommand) {
136147
$command->resetComposer();
137148
}
138149
}
139150
}
140151

152+
/**
153+
* @param $dir
154+
* @return void
155+
*/
141156
private function chdir($dir)
142157
{
143158
chdir($dir);
144-
$this->getIO()->writeError('<info>Changed current directory to ' . $dir . '</info>', true, IOInterface::VERBOSE);
159+
160+
$this->getIO()->writeError(
161+
sprintf('<info>Changed current directory to %s</info>', $dir),
162+
true,
163+
IOInterface::VERBOSE
164+
);
145165
}
146166

167+
/**
168+
* @return \Composer\Config
169+
* @throws \Composer\Json\JsonValidationException
170+
* @throws \Seld\JsonLint\ParsingException
171+
*/
147172
private function createConfig()
148173
{
149174
$config = Factory::createConfig();

src/Config.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public function __construct(Composer $composer)
1515
[
1616
'bin-links' => true,
1717
'target-directory' => 'vendor-bin',
18+
'forward-command' => false,
1819
],
1920
isset($extra['bamarni-bin']) ? $extra['bamarni-bin'] : []
2021
);
@@ -35,4 +36,12 @@ public function getTargetDirectory()
3536
{
3637
return $this->config['target-directory'];
3738
}
39+
40+
/**
41+
* @return bool
42+
*/
43+
public function isCommandForwarded()
44+
{
45+
return $this->config['forward-command'];
46+
}
3847
}

src/Plugin.php

Lines changed: 90 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,40 @@
33
namespace Bamarni\Composer\Bin;
44

55
use Composer\Composer;
6+
use Composer\Console\Application;
7+
use Composer\EventDispatcher\EventSubscriberInterface;
68
use Composer\IO\IOInterface;
9+
use Composer\Plugin\CommandEvent;
10+
use Composer\Plugin\PluginEvents;
711
use Composer\Plugin\PluginInterface;
812
use Composer\Plugin\Capable;
13+
use Symfony\Component\Console\Input\ArrayInput;
14+
use Symfony\Component\Console\Input\InputArgument;
15+
use Symfony\Component\Console\Input\InputDefinition;
916

10-
class Plugin implements PluginInterface, Capable
17+
class Plugin implements PluginInterface, Capable, EventSubscriberInterface
1118
{
19+
/**
20+
* @var Composer
21+
*/
22+
protected $composer;
23+
24+
/**
25+
* @var IOInterface
26+
*/
27+
protected $io;
28+
1229
/**
1330
* @return void
1431
*/
1532
public function activate(Composer $composer, IOInterface $io)
1633
{
34+
$this->composer = $composer;
35+
$this->io = $io;
1736
}
1837

1938
/**
20-
* @return array
39+
* @return string[]
2140
*/
2241
public function getCapabilities()
2342
{
@@ -39,4 +58,73 @@ public function deactivate(Composer $composer, IOInterface $io)
3958
public function uninstall(Composer $composer, IOInterface $io)
4059
{
4160
}
61+
62+
/**
63+
* @return string[]
64+
*/
65+
public static function getSubscribedEvents()
66+
{
67+
return [
68+
PluginEvents::COMMAND => 'onCommandEvent',
69+
];
70+
}
71+
72+
/**
73+
* @param CommandEvent $event
74+
* @return bool
75+
*/
76+
public function onCommandEvent(CommandEvent $event)
77+
{
78+
$config = new Config($this->composer);
79+
80+
if ($config->isCommandForwarded()) {
81+
switch ($event->getCommandName()) {
82+
case 'update':
83+
case 'install':
84+
return $this->onCommandEventInstallUpdate($event);
85+
}
86+
}
87+
88+
return true;
89+
}
90+
91+
/**
92+
* @param CommandEvent $event
93+
* @return bool
94+
*/
95+
protected function onCommandEventInstallUpdate(CommandEvent $event)
96+
{
97+
$command = new BinCommand();
98+
$command->setComposer($this->composer);
99+
$command->setApplication(new Application());
100+
101+
$arguments = [
102+
'command' => $command->getName(),
103+
'namespace' => 'all',
104+
'args' => [],
105+
];
106+
107+
foreach (array_filter($event->getInput()->getArguments()) as $argument) {
108+
$arguments['args'][] = $argument;
109+
}
110+
111+
foreach (array_keys(array_filter($event->getInput()->getOptions())) as $option) {
112+
$arguments['args'][] = '--' . $option;
113+
}
114+
115+
$definition = new InputDefinition();
116+
$definition->addArgument(new InputArgument('command', InputArgument::REQUIRED));
117+
$definition->addArguments($command->getDefinition()->getArguments());
118+
$definition->addOptions($command->getDefinition()->getOptions());
119+
120+
$input = new ArrayInput($arguments, $definition);
121+
122+
try {
123+
$returnCode = $command->run($input, $event->getOutput());
124+
} catch (\Exception $e) {
125+
return false;
126+
}
127+
128+
return $returnCode === 0;
129+
}
42130
}

0 commit comments

Comments
 (0)