Skip to content

Commit b0c3ead

Browse files
andrewbessAndrii Beziazychnyi
authored andcommitted
Fixed issue #19481: Magento 2.3.0: After composer installation sampledata can't be installed from command line
1 parent 715d9de commit b0c3ead

File tree

4 files changed

+316
-111
lines changed

4 files changed

+316
-111
lines changed

app/code/Magento/SampleData/Console/Command/SampleDataDeployCommand.php

Lines changed: 68 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,17 @@
77
namespace Magento\SampleData\Console\Command;
88

99
use Composer\Console\Application;
10+
use Composer\Console\ApplicationFactory;
11+
use Exception;
1012
use Magento\Framework\App\Filesystem\DirectoryList;
13+
use Magento\Framework\Exception\FileSystemException;
14+
use Magento\Framework\Exception\LocalizedException;
15+
use Magento\Framework\Filesystem;
16+
use Magento\SampleData\Model\Dependency;
1117
use Magento\Setup\Model\PackagesAuth;
1218
use Symfony\Component\Console\Command\Command;
1319
use Symfony\Component\Console\Input\ArrayInput;
20+
use Symfony\Component\Console\Input\ArrayInputFactory;
1421
use Symfony\Component\Console\Input\InputInterface;
1522
use Symfony\Component\Console\Input\InputOption;
1623
use Symfony\Component\Console\Output\OutputInterface;
@@ -23,37 +30,37 @@ class SampleDataDeployCommand extends Command
2330
const OPTION_NO_UPDATE = 'no-update';
2431

2532
/**
26-
* @var \Magento\Framework\Filesystem
33+
* @var Filesystem
2734
*/
2835
private $filesystem;
2936

3037
/**
31-
* @var \Magento\SampleData\Model\Dependency
38+
* @var Dependency
3239
*/
3340
private $sampleDataDependency;
3441

3542
/**
36-
* @var \Symfony\Component\Console\Input\ArrayInputFactory
43+
* @var ArrayInputFactory
3744
* @deprecated 100.1.0
3845
*/
3946
private $arrayInputFactory;
4047

4148
/**
42-
* @var \Composer\Console\ApplicationFactory
49+
* @var ApplicationFactory
4350
*/
4451
private $applicationFactory;
4552

4653
/**
47-
* @param \Magento\Framework\Filesystem $filesystem
48-
* @param \Magento\SampleData\Model\Dependency $sampleDataDependency
49-
* @param \Symfony\Component\Console\Input\ArrayInputFactory $arrayInputFactory
50-
* @param \Composer\Console\ApplicationFactory $applicationFactory
54+
* @param Filesystem $filesystem
55+
* @param Dependency $sampleDataDependency
56+
* @param ArrayInputFactory $arrayInputFactory
57+
* @param ApplicationFactory $applicationFactory
5158
*/
5259
public function __construct(
53-
\Magento\Framework\Filesystem $filesystem,
54-
\Magento\SampleData\Model\Dependency $sampleDataDependency,
55-
\Symfony\Component\Console\Input\ArrayInputFactory $arrayInputFactory,
56-
\Composer\Console\ApplicationFactory $applicationFactory
60+
Filesystem $filesystem,
61+
Dependency $sampleDataDependency,
62+
ArrayInputFactory $arrayInputFactory,
63+
ApplicationFactory $applicationFactory
5764
) {
5865
$this->filesystem = $filesystem;
5966
$this->sampleDataDependency = $sampleDataDependency;
@@ -63,7 +70,7 @@ public function __construct(
6370
}
6471

6572
/**
66-
* {@inheritdoc}
73+
* @inheritdoc
6774
*/
6875
protected function configure()
6976
{
@@ -79,15 +86,42 @@ protected function configure()
7986
}
8087

8188
/**
82-
* {@inheritdoc}
89+
* @inheritdoc
90+
*
91+
* @param InputInterface $input
92+
* @param OutputInterface $output
93+
* @return int|void
94+
* @throws FileSystemException
95+
* @throws LocalizedException
8396
*/
8497
protected function execute(InputInterface $input, OutputInterface $output)
8598
{
86-
$rootJson = json_decode($this->filesystem->getDirectoryRead(DirectoryList::ROOT)->readFile("composer.json"));
99+
$rootJson = json_decode(
100+
$this->filesystem->getDirectoryRead(
101+
DirectoryList::ROOT
102+
)->readFile("composer.json")
103+
);
87104
if (!isset($rootJson->version)) {
88-
// @codingStandardsIgnoreLine
89-
$output->writeln('<info>' . 'Git installations must deploy sample data from GitHub; see https://devdocs.magento.com/guides/v2.3/install-gde/install/sample-data-after-clone.html for more information.' . '</info>');
90-
return;
105+
$magentoProductPackage = array_filter(
106+
(array) $rootJson->require,
107+
function ($package) {
108+
return false !== strpos($package, 'magento/product-');
109+
},
110+
ARRAY_FILTER_USE_KEY
111+
);
112+
$version = reset($magentoProductPackage);
113+
$output->writeln(
114+
'<info>' .
115+
// @codingStandardsIgnoreLine
116+
'We don\'t recommend to remove the "version" field from your composer.json; see https://getcomposer.org/doc/02-libraries.md#library-versioning for more information.' .
117+
'</info>'
118+
);
119+
$restoreVersion = new ArrayInput([
120+
'command' => 'config',
121+
'setting-key' => 'version',
122+
'setting-value' => [$version],
123+
'--quiet' => 1
124+
]);
91125
}
92126
$this->updateMemoryLimit();
93127
$this->createAuthFile();
@@ -109,6 +143,12 @@ protected function execute(InputInterface $input, OutputInterface $output)
109143
/** @var Application $application */
110144
$application = $this->applicationFactory->create();
111145
$application->setAutoExit(false);
146+
if (!empty($restoreVersion)) {
147+
$result = $application->run($restoreVersion, clone $output);
148+
if ($result === 0) {
149+
$output->writeln('<info>The field "version" has been restored.</info>');
150+
}
151+
}
112152
$result = $application->run($commandInput, $output);
113153
if ($result !== 0) {
114154
$output->writeln(
@@ -128,7 +168,7 @@ protected function execute(InputInterface $input, OutputInterface $output)
128168
* We create auth.json with correct permissions instead of relying on Composer.
129169
*
130170
* @return void
131-
* @throws \Exception
171+
* @throws LocalizedException
132172
*/
133173
private function createAuthFile()
134174
{
@@ -137,16 +177,18 @@ private function createAuthFile()
137177
if (!$directory->isExist(PackagesAuth::PATH_TO_AUTH_FILE)) {
138178
try {
139179
$directory->writeFile(PackagesAuth::PATH_TO_AUTH_FILE, '{}');
140-
} catch (\Exception $e) {
141-
$message = 'Error in writing Auth file '
142-
. $directory->getAbsolutePath(PackagesAuth::PATH_TO_AUTH_FILE)
143-
. '. Please check permissions for writing.';
144-
throw new \Exception($message);
180+
} catch (Exception $e) {
181+
throw new LocalizedException(__(
182+
'Error in writing Auth file %1. Please check permissions for writing.',
183+
$directory->getAbsolutePath(PackagesAuth::PATH_TO_AUTH_FILE)
184+
));
145185
}
146186
}
147187
}
148188

149189
/**
190+
* Updates PHP memory limit
191+
*
150192
* @return void
151193
*/
152194
private function updateMemoryLimit()
@@ -161,6 +203,8 @@ private function updateMemoryLimit()
161203
}
162204

163205
/**
206+
* Retrieves the memory size in bytes
207+
*
164208
* @param string $value
165209
* @return int
166210
*/

app/code/Magento/SampleData/Test/Unit/Console/Command/AbstractSampleDataCommandTest.php

Lines changed: 89 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
declare(strict_types=1);
7+
68
namespace Magento\SampleData\Test\Unit\Console\Command;
79

810
use Composer\Console\Application;
@@ -12,54 +14,64 @@
1214
use Magento\Framework\Filesystem\Directory\ReadInterface;
1315
use Magento\Framework\Filesystem\Directory\WriteInterface;
1416
use Magento\SampleData\Model\Dependency;
17+
use PHPUnit\Framework\MockObject\MockObject;
1518
use PHPUnit\Framework\TestCase;
1619
use Symfony\Component\Console\Input\ArrayInput;
1720
use Symfony\Component\Console\Input\ArrayInputFactory;
1821

1922
/**
23+
* Common class for tests
24+
*
2025
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
2126
*/
2227
abstract class AbstractSampleDataCommandTest extends TestCase
2328
{
2429
/**
25-
* @var ReadInterface|\PHPUnit_Framework_MockObject_MockObject
30+
* @var ReadInterface|MockObject
2631
*/
2732
protected $directoryReadMock;
2833

2934
/**
30-
* @var WriteInterface|\PHPUnit_Framework_MockObject_MockObject
35+
* @var WriteInterface|MockObject
3136
*/
3237
protected $directoryWriteMock;
3338

3439
/**
35-
* @var Filesystem|\PHPUnit_Framework_MockObject_MockObject
40+
* @var Filesystem|MockObject
3641
*/
3742
protected $filesystemMock;
3843

3944
/**
40-
* @var Dependency|\PHPUnit_Framework_MockObject_MockObject
45+
* @var Dependency|MockObject
4146
*/
4247
protected $sampleDataDependencyMock;
4348

4449
/**
45-
* @var ArrayInputFactory|\PHPUnit_Framework_MockObject_MockObject
50+
* @var ArrayInputFactory|MockObject
4651
*/
4752
protected $arrayInputFactoryMock;
4853

4954
/**
50-
* @var Application|\PHPUnit_Framework_MockObject_MockObject
55+
* @var Application|MockObject
5156
*/
5257
protected $applicationMock;
5358

5459
/**
55-
* @var ApplicationFactory|\PHPUnit_Framework_MockObject_MockObject
60+
* @var ApplicationFactory|MockObject
5661
*/
5762
protected $applicationFactoryMock;
5863

5964
/**
65+
* @var int
66+
*/
67+
private $appRunResult;
68+
69+
/**
70+
* Creates mocks
71+
*
6072
* @return void
6173
*/
62-
protected function setUp()
74+
protected function setUp(): void
6375
{
6476
$this->directoryReadMock = $this->createMock(ReadInterface::class);
6577
$this->directoryWriteMock = $this->createMock(WriteInterface::class);
@@ -71,47 +83,86 @@ protected function setUp()
7183
}
7284

7385
/**
74-
* @param array $sampleDataPackages Array in form [package_name => version_constraint]
75-
* @param string $pathToComposerJson Fake path to composer.json
76-
* @param int $appRunResult Composer exit code
86+
* Sets mocks
87+
*
88+
* @param array $sampleDataPackages Array in form [package_name => version_constraint]
89+
* @param string $pathToComposerJson Fake path to composer.json
90+
* @param int $appRunResult Composer exit code
91+
* @param array $composerJsonContent Content of the composer.json
7792
* @param array $additionalComposerArgs Additional arguments that composer expects
7893
*/
7994
protected function setupMocks(
8095
$sampleDataPackages,
8196
$pathToComposerJson,
8297
$appRunResult,
98+
$composerJsonContent = [],
8399
$additionalComposerArgs = []
84100
) {
85-
$this->directoryReadMock->expects($this->any())->method('getAbsolutePath')->willReturn($pathToComposerJson);
86-
$this->directoryReadMock->expects($this->any())->method('readFile')->with('composer.json')->willReturn(
87-
'{"version": "0.0.1"}'
88-
);
89-
$this->filesystemMock->expects($this->any())->method('getDirectoryRead')->with(DirectoryList::ROOT)->willReturn(
90-
$this->directoryReadMock
91-
);
92-
$this->sampleDataDependencyMock->expects($this->any())->method('getSampleDataPackages')->willReturn(
93-
$sampleDataPackages
94-
);
101+
$this->appRunResult = $appRunResult;
102+
$this->directoryReadMock->expects($this->any())
103+
->method('getAbsolutePath')
104+
->willReturn($pathToComposerJson);
105+
$this->directoryReadMock->expects($this->any())
106+
->method('readFile')
107+
->with('composer.json')
108+
->willReturn(json_encode($composerJsonContent));
109+
$this->filesystemMock->expects($this->any())
110+
->method('getDirectoryRead')
111+
->with(DirectoryList::ROOT)
112+
->willReturn($this->directoryReadMock);
113+
$this->sampleDataDependencyMock->expects($this->any())
114+
->method('getSampleDataPackages')
115+
->willReturn($sampleDataPackages);
95116
$this->arrayInputFactoryMock->expects($this->never())->method('create');
96117

97-
$this->applicationMock->expects($this->any())
98-
->method('run')
99-
->with(
100-
new ArrayInput(
101-
array_merge(
102-
$this->expectedComposerArguments(
103-
$sampleDataPackages,
104-
$pathToComposerJson
118+
if (!array_key_exists('version', $composerJsonContent)) {
119+
$this->applicationMock->expects($this->any())
120+
->method('run')
121+
->withConsecutive(
122+
[
123+
'input' => new ArrayInput(
124+
$this->expectedComposerArgumentsCommandConfig()
105125
),
106-
$additionalComposerArgs
107-
)
108-
),
109-
$this->anything()
110-
)
111-
->willReturn($appRunResult);
126+
'output' => $this->anything()
127+
],
128+
[
129+
'input' => new ArrayInput(
130+
array_merge(
131+
$this->expectedComposerArgumentsSampleDataCommands(
132+
$sampleDataPackages,
133+
$pathToComposerJson
134+
),
135+
$additionalComposerArgs
136+
)
137+
),
138+
'output' => $this->anything()
139+
]
140+
)->willReturnOnConsecutiveCalls(
141+
$this->returnValue(0),
142+
$this->returnValue($appRunResult)
143+
);
144+
} else {
145+
$this->applicationMock->expects($this->any())
146+
->method('run')
147+
->with(
148+
new ArrayInput(
149+
array_merge(
150+
$this->expectedComposerArgumentsSampleDataCommands(
151+
$sampleDataPackages,
152+
$pathToComposerJson
153+
),
154+
$additionalComposerArgs
155+
)
156+
),
157+
$this->anything()
158+
)
159+
->willReturn($appRunResult);
160+
}
112161

113162
if (($appRunResult !== 0) && !empty($sampleDataPackages)) {
114-
$this->applicationMock->expects($this->once())->method('resetComposer')->willReturnSelf();
163+
$this->applicationMock->expects($this->any())
164+
->method('resetComposer')
165+
->willReturnSelf();
115166
}
116167

117168
$this->applicationFactoryMock->expects($this->any())
@@ -120,13 +171,13 @@ protected function setupMocks(
120171
}
121172

122173
/**
123-
* Expected arguments for composer based on sample data packages and composer.json path
174+
* Expected arguments for composer based on sample data command
124175
*
125176
* @param array $sampleDataPackages
126177
* @param string $pathToComposerJson
127178
* @return array
128179
*/
129-
abstract protected function expectedComposerArguments(
180+
abstract protected function expectedComposerArgumentsSampleDataCommands(
130181
array $sampleDataPackages,
131182
string $pathToComposerJson
132183
) : array;

0 commit comments

Comments
 (0)