Skip to content

Commit 786544b

Browse files
authored
Merge pull request #1177 from veewee/env-aware-task-compiler
Change the task compiler logic to allow resolving of environment variables.
2 parents 5f5d566 + a0202ce commit 786544b

17 files changed

Lines changed: 280 additions & 183 deletions

File tree

resources/config/services.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ services:
99
GrumPHP\Event\Dispatcher\Bridge\SymfonyEventDispatcher:
1010
arguments:
1111
- '@Symfony\Component\EventDispatcher\EventDispatcher'
12-
GrumPHP\Configuration\Resolver\TaskConfigResolver:
13-
synthetic: true
1412

1513
GrumPHP\Event\Dispatcher\EventDispatcherInterface: '@GrumPHP\Event\Dispatcher\Bridge\SymfonyEventDispatcher'
1614

spec/Collection/TasksCollectionSpec.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,19 @@ function it_can_filter_by_testsuite(TaskInterface $task1, TaskInterface $task2,
5454
$tasks[0]->shouldBe($task1);
5555
}
5656

57+
function it_can_filter_enabled(TaskInterface $task1, TaskInterface $task2, TestSuiteInterface $testSuite)
58+
{
59+
$task1->getConfig()->willReturn(new TaskConfig('task1', [], new Metadata(['enabled' => true])));
60+
$task2->getConfig()->willReturn(new TaskConfig('task2', [], new Metadata(['enabled' => false])));
61+
$testSuite->getTaskNames()->willReturn(['task1']);
62+
63+
$result = $this->filterEnabled($testSuite);
64+
$result->shouldBeAnInstanceOf(TasksCollection::class);
65+
$result->count()->shouldBe(1);
66+
$tasks = $result->toArray();
67+
$tasks[0]->shouldBe($task1);
68+
}
69+
5770
function it_can_filter_by_empty_testsuite(TaskInterface $task1, TaskInterface $task2)
5871
{
5972
$result = $this->filterByTestSuite(null);

spec/Configuration/Configurator/TaskConfiguratorSpec.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace spec\GrumPHP\Configuration\Configurator;
44

5+
use GrumPHP\Configuration\Resolver\TaskConfigResolver;
56
use GrumPHP\Task\Config\TaskConfigInterface;
67
use GrumPHP\Task\TaskInterface;
78
use PhpSpec\ObjectBehavior;
@@ -17,15 +18,17 @@ public function it_is_initializable(): void
1718
public function it_can_configure_a_task(
1819
TaskInterface $originalTask,
1920
TaskInterface $expectedTask,
20-
TaskConfigInterface $config
21+
TaskConfigInterface $config,
22+
TaskConfigResolver $taskConfigResolver,
2123
): void {
24+
$taskConfigResolver->resolve('taskName')->willReturn($config->getWrappedObject());
2225
$originalTask->withConfig($config)->will(function ($arguments) use ($expectedTask) {
2326
$expectedTask->getConfig()->willReturn($arguments[0]);
2427

2528
return $expectedTask;
2629
});
2730

28-
$result = $this->__invoke($originalTask, $config);
31+
$result = $this->__invoke($originalTask, $taskConfigResolver, 'taskName');
2932
$result->shouldNotBe($originalTask);
3033
$result->shouldBe($expectedTask);
3134
$result->getConfig()->shouldBe($config);

spec/Configuration/Resolver/TaskConfigResolverSpec.php

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
namespace spec\GrumPHP\Configuration\Resolver;
44

55
use GrumPHP\Task\Config\ConfigOptionsResolver;
6-
use const GrumPHP\Exception\TaskConfigResolverException;
76
use GrumPHP\Exception\TaskConfigResolverException;
87
use GrumPHP\Runner\TaskResult;
98
use GrumPHP\Runner\TaskResultInterface;
109
use GrumPHP\Task\Config\EmptyTaskConfig;
10+
use GrumPHP\Task\Config\Metadata;
11+
use GrumPHP\Task\Config\TaskConfig;
1112
use GrumPHP\Task\Config\TaskConfigInterface;
1213
use GrumPHP\Task\Context\ContextInterface;
1314
use GrumPHP\Task\TaskInterface;
@@ -30,40 +31,67 @@ public function it_is_initializable(): void
3031
public function it_can_list_task_names(): void
3132
{
3233
$this->beConstructedWith([
33-
'task1' => get_class($this->mockTask()),
34-
'task2' => get_class($this->mockTask()),
34+
'task1' => [
35+
'class' => get_class($this->mockTask()),
36+
'config' => [],
37+
],
38+
'task2' => [
39+
'class' => get_class($this->mockTask()),
40+
'config' => [],
41+
],
3542
]);
3643
$this->listAvailableTaskNames()->shouldBe(['task1', 'task2']);
3744
}
3845

39-
public function it_can_resolve_config_for_task_without_metadata(): void
46+
public function it_can_resolve_config_for_task(): void
4047
{
4148
$task1 = $this->mockTask();
42-
$this->beConstructedWith([$taskName = 'task1' => get_class($task1)]);
43-
$this->resolve('task1', ['metadata' => ['label' => 'hello']])->shouldBe([
44-
'class' => get_class($task1),
49+
$taskName = 'task1';
50+
$this->beConstructedWith([
51+
$taskName => [
52+
'class' => get_class($task1),
53+
'config' => [
54+
'foo' => 'hello',
55+
'metadata' => ['priority' => 1000],
56+
]
57+
]
4558
]);
59+
60+
$actual = $this->resolve($taskName);
61+
$actual->getName()->shouldBe($taskName);
62+
$actual->getOptions()->shouldBe(['foo' => 'hello']);
63+
$actual->getMetadata()->priority()->shouldBe(1000);
4664
}
4765

48-
public function it_fetches_resolver_for_task(): void
66+
public function it_can_fetch_config_resolver_for_task(): void
4967
{
5068
$task1 = $this->mockTask();
51-
$this->beConstructedWith([$taskName = 'task1' => get_class($task1)]);
52-
$result = $this->fetchByName($taskName)->resolve([]);
53-
$result->shouldBeLike($task1::getConfigurableOptions()->resolve([]));
69+
$taskName = 'task1';
70+
$this->beConstructedWith([
71+
$taskName => [
72+
'class' => get_class($task1),
73+
'config' => [
74+
'foo' => 'hello',
75+
'metadata' => ['priority' => 1000],
76+
]
77+
]
78+
]);
79+
80+
$actual = $this->fetchByName($taskName);
81+
$actual->resolve(['foo' => 'hello'])->shouldBe(['foo' => 'hello']);
5482
}
5583

5684
public function it_fails_when_task_is_unknown(): void
5785
{
58-
$this->beConstructedWith(['task1' => get_class($this->mockTask())]);
86+
$this->beConstructedWith(['task1' => ['class' => get_class($this->mockTask())]]);
5987
$this->shouldThrow(TaskConfigResolverException::class)->duringFetchByName('task2');
6088
}
6189

6290
public function it_fails_when_task_is_not_a_grumphp_task(): void
6391
{
6492
$this->beConstructedWith([
65-
'task1' => get_class(new class() {}),
66-
'task2' => 'Some\\Mega\\Unknown\\Class\\PLease\\Dont\\Create\\Me',
93+
'task1' => ['class' => get_class(new class() {}), 'config' => []],
94+
'task2' => ['class' => 'Some\\Mega\\Unknown\\Class\\PLease\\Dont\\Create\\Me', 'config' => []],
6795
]);
6896
$this->shouldThrow(TaskConfigResolverException::class)->duringFetchByName('task1');
6997
$this->shouldThrow(TaskConfigResolverException::class)->duringFetchByName('task2');
@@ -76,7 +104,7 @@ private function mockTask(): TaskInterface
76104
public static function getConfigurableOptions(): ConfigOptionsResolver
77105
{
78106
$options = new OptionsResolver();
79-
$options->setDefault('class', static::class);
107+
$options->setDefault('foo', 'bar');
80108
return ConfigOptionsResolver::fromOptionsResolver($options);
81109
}
82110

spec/Task/Config/LazyTaskConfigSpec.php

Lines changed: 0 additions & 61 deletions
This file was deleted.

src/Collection/TasksCollection.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ public function filterByTaskNames(array $tasks): self
4747
});
4848
}
4949

50+
public function filterEnabled(): self
51+
{
52+
return $this->filter(
53+
static fn(TaskInterface $task): bool => $task->getConfig()->getMetadata()->isEnabled()
54+
);
55+
}
56+
5057
/**
5158
* This method sorts the tasks by highest priority first.
5259
*/

src/Configuration/Compiler/TaskCompilerPass.php

Lines changed: 22 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,6 @@
88
use GrumPHP\Configuration\Configurator\TaskConfigurator;
99
use GrumPHP\Configuration\Resolver\TaskConfigResolver;
1010
use GrumPHP\Exception\TaskConfigResolverException;
11-
use GrumPHP\Task\Config\LazyTaskConfig;
12-
use GrumPHP\Task\Config\Metadata;
13-
use GrumPHP\Task\Config\TaskConfig;
1411
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
1512
use Symfony\Component\DependencyInjection\ContainerBuilder;
1613
use Symfony\Component\DependencyInjection\Definition;
@@ -30,7 +27,7 @@ public function process(ContainerBuilder $container): void
3027
$availableTasks = $this->fetchAvailableTasksInfo($container);
3128
$configuredTasks = $container->getParameter('tasks');
3229
$configuredTasks = is_array($configuredTasks) ? $configuredTasks : [];
33-
$taskConfigResolver = $this->buildTaskConfigResolver($availableTasks);
30+
$taskResolverConfig = [];
3431

3532
// Configure tasks
3633
foreach ($configuredTasks as $taskName => $config) {
@@ -46,30 +43,27 @@ public function process(ContainerBuilder $container): void
4643
['id' => $taskId, 'class' => $taskClass, 'info' => $taskInfo] = $currentTaskService;
4744
$configuredTaskKey = $taskId.'.'.$taskName.'.configured';
4845

49-
// Setup metadata:
50-
$metadata = new Metadata(array_merge(
51-
['priority' => $taskInfo['priority']],
52-
$metadataConfig
53-
));
54-
55-
// Disabled tasks can be skipped
56-
// This allows to conditionally disable tasks through parameters or by an extension.
57-
if (!$metadata->isEnabled()) {
58-
continue;
59-
}
46+
// Store the configuration in the task resolver config:
47+
// This way, the resolver knows how to build all task related configurations.
48+
// It is stores in a pain array so that env variables get resolved and can be used in the configuration.
49+
$taskResolverConfig[$taskName] = [
50+
'class' => $taskClass,
51+
'config' => array_merge(
52+
$taskConfig,
53+
[
54+
'metadata' => array_merge(
55+
['priority' => $taskInfo['priority']],
56+
$taskConfig['metadata'] ?? [],
57+
),
58+
],
59+
)
60+
];
6061

6162
// Configure task:
6263
$taskBuilder = new Definition($taskClass, [
6364
new Reference($taskId),
64-
new LazyTaskConfig(
65-
function () use ($taskName, $taskConfigResolver, $currentTaskName, $taskConfig, $metadata) {
66-
return new TaskConfig(
67-
$taskName,
68-
$taskConfigResolver->resolve($currentTaskName, $taskConfig),
69-
$metadata
70-
);
71-
}
72-
)
65+
new Reference(TaskConfigResolver::class),
66+
$taskName,
7367
]);
7468
$taskBuilder->setFactory([new Reference(TaskConfigurator::class), '__invoke']);
7569
$taskBuilder->addTag('configured.task');
@@ -80,7 +74,10 @@ function () use ($taskName, $taskConfigResolver, $currentTaskName, $taskConfig,
8074
}
8175

8276
// Register available and configured tasks for easy data usage in the application:
83-
$container->set(TaskConfigResolver::class, $taskConfigResolver);
77+
$container->setDefinition(TaskConfigResolver::class, new Definition(
78+
TaskConfigResolver::class,
79+
[$taskResolverConfig]
80+
));
8481
$container->setParameter('grumphp.tasks.configured', array_keys($configuredTasks));
8582
}
8683

@@ -127,16 +124,4 @@ private function fetchAvailableTasksInfo(ContainerBuilder $container): array
127124

128125
return $map;
129126
}
130-
131-
private function buildTaskConfigResolver(array $availableTasks): TaskConfigResolver
132-
{
133-
return new TaskConfigResolver(
134-
array_map(
135-
function ($availableTask): string {
136-
return (string) ($availableTask['class'] ?? '');
137-
},
138-
$availableTasks
139-
)
140-
);
141-
}
142127
}

src/Configuration/Configurator/TaskConfigurator.php

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44

55
namespace GrumPHP\Configuration\Configurator;
66

7-
use GrumPHP\Task\Config\TaskConfigInterface;
7+
use GrumPHP\Configuration\Resolver\TaskConfigResolver;
88
use GrumPHP\Task\TaskInterface;
99

1010
class TaskConfigurator
1111
{
12-
public function __invoke(TaskInterface $task, TaskConfigInterface $config): TaskInterface
13-
{
14-
return $task->withConfig($config);
12+
public function __invoke(
13+
TaskInterface $task,
14+
TaskConfigResolver $configResolver,
15+
string $taskName
16+
): TaskInterface {
17+
return $task->withConfig(
18+
$configResolver->resolve($taskName)
19+
);
1520
}
1621
}

src/Configuration/ContainerFactory.php

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace GrumPHP\Configuration;
66

7-
use GrumPHP\Configuration\Environment\DotEnvRegistrar;
87
use GrumPHP\Configuration\Environment\PathsRegistrar;
98
use GrumPHP\Configuration\Model\EnvConfig;
109
use GrumPHP\Locator\EnrichedGuessedPathsFromDotEnvLocator;
@@ -27,12 +26,9 @@ public static function build(InputInterface $input, OutputInterface $output): Co
2726
// Build the service container:
2827
$container = ContainerBuilder::buildFromConfiguration($guessedPaths->getConfigFile());
2928

30-
// Load environment config:
29+
// Apply environment configurations onto guessed paths:
3130
$config = $container->get(EnvConfig::class);
3231
assert($config instanceof EnvConfig);
33-
34-
// Set up the environment and overwrite guessed paths if needed:
35-
DotEnvRegistrar::register($config);
3632
$guessedPaths = self::enrichGuessedPathsWithDotEnv($container, $guessedPaths);
3733

3834
// Make sure that important paths are loaded first:

0 commit comments

Comments
 (0)