initializeVendorDir();
$this->annoy( $io );
$extra = $composer->getPackage()->getExtra();
if (isset($extra['magento-root-dir']) || $rootDirInput = $this->defaultMagentoRootDir) {
if (isset($rootDirInput)) {
$extra['magento-root-dir'] = $rootDirInput;
}
$dir = rtrim(trim($extra['magento-root-dir']), '/\\');
$this->magentoRootDir = new \SplFileInfo($dir);
if (!is_dir($dir) && $io->askConfirmation('magento root dir "' . $dir . '" missing! create now? [Y,n] ')) {
$this->initializeMagentoRootDir($dir);
$io->write('magento root dir "' . $dir . '" created');
}
if (!is_dir($dir)) {
$dir = $this->vendorDir . "/$dir";
$this->magentoRootDir = new \SplFileInfo($dir);
}
}
if (isset($extra['modman-root-dir'])) {
$dir = rtrim(trim($extra['modman-root-dir']), '/\\');
if (!is_dir($dir)) {
$dir = $this->vendorDir . "/$dir";
}
if (!is_dir($dir)) {
throw new \ErrorException("modman root dir \"{$dir}\" is not valid");
}
$this->modmanRootDir = new \SplFileInfo($dir);
}
if (isset($extra['magento-deploystrategy'])) {
$this->_deployStrategy = (string)$extra['magento-deploystrategy'];
if($this->_deployStrategy !== "copy"){
$io->write("Warning: Magento 2 is not tested with \"{$this->_deployStrategy}\" deployment strategy. It may not function properly.");
}
}
if ((is_null($this->magentoRootDir) || false === $this->magentoRootDir->isDir())
&& $this->_deployStrategy != 'none'
) {
$dir = $this->magentoRootDir instanceof \SplFileInfo ? $this->magentoRootDir->getPathname() : '';
$io->write("magento root dir \"{$dir}\" is not valid", true);
$io->write('You need to set an existing path for "magento-root-dir" in your composer.json', true);
$io->write('For more information please read about the "Usage" in the README of the installer Package', true);
throw new \ErrorException("magento root dir \"{$dir}\" is not valid");
}
if (isset($extra['magento-force'])) {
$this->isForced = (bool)$extra['magento-force'];
}
if (false !== getenv('MAGENTO_CLOUD_PROJECT')) {
$this->setDeployStrategy('none');
}
if (isset($extra['magento-deploystrategy'])) {
$this->setDeployStrategy((string)$extra['magento-deploystrategy']);
}
if (!empty($extra['auto-append-gitignore'])) {
$this->appendGitIgnore = true;
}
if (!empty($extra['path-mapping-translations'])) {
$this->_pathMappingTranslations = (array)$extra['path-mapping-translations'];
}
}
/**
* @param DeployManager $deployManager
*/
public function setDeployManager( DeployManager $deployManager)
{
$this->deployManager = $deployManager;
}
public function setConfig( ProjectConfig $config )
{
$this->config = $config;
}
/**
* @return DeployManager
*/
public function getDeployManager()
{
return $this->deployManager;
}
/**
* Create base requrements for project installation
*/
protected function initializeMagentoRootDir() {
if (!$this->magentoRootDir->isDir()) {
$magentoRootPath = $this->magentoRootDir->getPathname();
$pathParts = explode(DIRECTORY_SEPARATOR, $magentoRootPath);
$baseDir = explode(DIRECTORY_SEPARATOR, $this->vendorDir);
array_pop($baseDir);
$pathParts = array_merge($baseDir, $pathParts);
$directoryPath = '';
foreach ($pathParts as $pathPart) {
$directoryPath .= $pathPart . DIRECTORY_SEPARATOR;
$this->filesystem->ensureDirectoryExists($directoryPath);
}
}
// $this->getSourceDir($package);
}
/**
*
* @param array $extra
* @param \Composer\IO\IOInterface $io
* @return int
*/
private function updateJsonExtra($extra, $io) {
$file = Factory::getComposerFile();
if (!file_exists($file) && !file_put_contents($file, "{\n}\n")) {
$io->write('' . $file . ' could not be created.');
return 1;
}
if (!is_readable($file)) {
$io->write('' . $file . ' is not readable.');
return 1;
}
if (!is_writable($file)) {
$io->write('' . $file . ' is not writable.');
return 1;
}
$json = new JsonFile($file);
$composer = $json->read();
$composerBackup = file_get_contents($json->getPath());
$extraKey = 'extra';
$baseExtra = array_key_exists($extraKey, $composer) ? $composer[$extraKey] : array();
if (!$this->updateFileCleanly($json, $baseExtra, $extra, $extraKey)) {
foreach ($extra as $key => $value) {
$baseExtra[$key] = $value;
}
$composer[$extraKey] = $baseExtra;
$json->write($composer);
}
}
private function updateFileCleanly($json, array $base, array $new, $rootKey) {
$contents = file_get_contents($json->getPath());
$manipulator = new JsonManipulator($contents);
foreach ($new as $childKey => $childValue) {
if (!$manipulator->addLink($rootKey, $childKey, $childValue)) {
return false;
}
}
file_put_contents($json->getPath(), $manipulator->getContents());
return true;
}
/**
* @param string $strategy
*/
public function setDeployStrategy($strategy)
{
$this->_deployStrategy = $strategy;
}
/**
* Returns the strategy class used for deployment
*
* @param \Composer\Package\PackageInterface $package
* @param string $strategy
* @return \MagentoHackathon\Composer\Magento\Deploystrategy\DeploystrategyAbstract
*/
public function getDeployStrategy(PackageInterface $package, $strategy = null)
{
if (null === $strategy) {
$strategy = $this->_deployStrategy;
}
$extra = $this->composer->getPackage()->getExtra();
if( isset($extra['magento-deploystrategy-overwrite']) ){
$moduleSpecificDeployStrategys = $this->transformArrayKeysToLowerCase($extra['magento-deploystrategy-overwrite']);
if( isset($moduleSpecificDeployStrategys[$package->getName()]) ){
$strategy = $moduleSpecificDeployStrategys[$package->getName()];
}
}
$moduleSpecificDeployIgnores = array();
if( isset($extra['magento-deploy-ignore']) ){
$extra['magento-deploy-ignore'] = $this->transformArrayKeysToLowerCase($extra['magento-deploy-ignore']);
if( isset($extra['magento-deploy-ignore']["*"]) ){
$moduleSpecificDeployIgnores = $extra['magento-deploy-ignore']["*"];
}
if( isset($extra['magento-deploy-ignore'][$package->getName()]) ){
$moduleSpecificDeployIgnores = array_merge(
$moduleSpecificDeployIgnores,
$extra['magento-deploy-ignore'][$package->getName()]
);
}
}
if($package->getType() === 'magento-core'){
$strategy = 'copy';
}
$targetDir = $this->getTargetDir();
$sourceDir = $this->getSourceDir($package);
switch ($strategy) {
case 'symlink':
$impl = new \MagentoHackathon\Composer\Magento\Deploystrategy\Symlink($sourceDir, $targetDir);
break;
case 'link':
$impl = new \MagentoHackathon\Composer\Magento\Deploystrategy\Link($sourceDir, $targetDir);
break;
case 'none':
$impl = new \MagentoHackathon\Composer\Magento\Deploystrategy\None($sourceDir, $targetDir);
break;
case 'copy':
default:
$impl = new \MagentoHackathon\Composer\Magento\Deploystrategy\Copy($sourceDir, $targetDir);
}
// Inject isForced setting from extra config
$impl->setIsForced($this->isForced);
$impl->setIgnoredMappings($moduleSpecificDeployIgnores);
return $impl;
}
/**
* Decides if the installer supports the given type
*
* @param string $packageType
* @return bool
*/
public function supports($packageType)
{
return array_key_exists($packageType, PackageTypes::$packageTypes);
}
/**
* Return Source dir of package
*
* @param \Composer\Package\PackageInterface $package
* @return string
*/
protected function getSourceDir(PackageInterface $package)
{
$this->filesystem->ensureDirectoryExists($this->vendorDir);
return $this->getInstallPath($package);
}
/**
* Return the absolute target directory path for package installation
*
* @return string
*/
public function getTargetDir()
{
$targetDir = realpath($this->magentoRootDir->getPathname());
return $targetDir;
}
/**
* Installs specific package
*
* @param InstalledRepositoryInterface $repo repository in which to check
* @param PackageInterface $package package instance
*/
public function install(InstalledRepositoryInterface $repo, PackageInterface $package)
{
if ($package->getType() === 'magento-core' && !$this->preInstallMagentoCore()) {
return;
}
parent::install($repo, $package);
// skip marshal and apply default behavior if extra->map does not exist
if (!$this->hasExtraMap($package)) {
return;
}
$strategy = $this->getDeployStrategy($package);
$strategy->setMappings($this->getParser($package)->getMappings());
$deployManagerEntry = new Entry();
$deployManagerEntry->setPackageName($package->getName());
$deployManagerEntry->setDeployStrategy($strategy);
$this->deployManager->addPackage($deployManagerEntry);
if ($this->appendGitIgnore) {
$this->appendGitIgnore($package, $this->getGitIgnoreFileLocation());
}
}
/**
* Get .gitignore file location
*
* @return string
*/
public function getGitIgnoreFileLocation()
{
$ignoreFile = $this->magentoRootDir->getPathname() . '/.gitignore';
return $ignoreFile;
}
/**
* Add all the files which are to be deployed
* to the .gitignore file, if it doesn't
* exist then create a new one
*
* @param PackageInterface $package
* @param string $ignoreFile
*/
public function appendGitIgnore(PackageInterface $package, $ignoreFile)
{
$contents = array();
if(file_exists($ignoreFile)) {
$contents = file($ignoreFile, FILE_IGNORE_NEW_LINES);
}
$additions = array();
foreach($this->getParser($package)->getMappings() as $map) {
$dest = $map[1];
$ignore = sprintf("/%s", $dest);
$ignore = str_replace('/./','/', $ignore);
$ignore = str_replace('//','/', $ignore);
$ignore = rtrim($ignore,'/');
if(!in_array($ignore, $contents)) {
$ignoredMappings = $this->getDeployStrategy($package)->getIgnoredMappings();
if( in_array($ignore, $ignoredMappings) ){
continue;
}
$additions[] = $ignore;
}
}
if(!empty($additions)) {
array_unshift($additions, '#' . $package->getName());
$contents = array_merge($contents, $additions);
file_put_contents($ignoreFile, implode("\n", $contents));
}
if ($package->getType() === 'magento-core') {
$this->prepareMagentoCore();
}
}
/**
* Install Magento core
*
* @param InstalledRepositoryInterface $repo repository in which to check
* @param PackageInterface $package package instance
*/
protected function preInstallMagentoCore() {
if (!$this->io->askConfirmation('Are you sure you want to install the Magento core?Attention: Your Magento root dir will be cleared in the process! [Y,n] ', true)) {
$this->io->write('Skipping core installation...');
return false;
}
$this->clearRootDir();
return true;
}
protected function clearRootDir() {
$this->filesystem->removeDirectory($this->magentoRootDir->getPathname());
$this->filesystem->ensureDirectoryExists($this->magentoRootDir->getPathname());
}
public function prepareMagentoCore() {
$this->setMagentoPermissions();
$this->redeployProject();
}
/**
* some directories have to be writable for the server
*/
protected function setMagentoPermissions() {
foreach ($this->_magentoWritableDirs as $dir) {
if (!file_exists($this->getTargetDir() . DIRECTORY_SEPARATOR . $dir)) {
mkdir($this->getTargetDir() . DIRECTORY_SEPARATOR . $dir, 0777, true);
}
$this->setPermissions($this->getTargetDir() . DIRECTORY_SEPARATOR . $dir, 0777, 0666);
}
}
/**
* set permissions recursively
*
* @param string $path Path to set permissions for
* @param int $dirmode Permissions to be set for directories
* @param int $filemode Permissions to be set for files
*/
protected function setPermissions($path, $dirmode, $filemode) {
if (is_dir($path)) {
if (!@chmod($path, $dirmode)) {
$this->io->write(
'Failed to set permissions "%s" for directory "%s"', decoct($dirmode), $path
);
}
$dh = opendir($path);
while (($file = readdir($dh)) !== false) {
if ($file != '.' && $file != '..') { // skip self and parent pointing directories
$fullpath = $path . '/' . $file;
$this->setPermissions($fullpath, $dirmode, $filemode);
}
}
closedir($dh);
} elseif (is_file($path)) {
if (false == !@chmod($path, $filemode)) {
$this->io->write(
'Failed to set permissions "%s" for file "%s"', decoct($filemode), $path
);
}
}
}
protected function redeployProject() {
$ioInterface = $this->io;
// init repos
$composer = $this->composer;
$installedRepo = $composer->getRepositoryManager()->getLocalRepository();
$dm = $composer->getDownloadManager();
$im = $composer->getInstallationManager();
/*
* @var $moduleInstaller MagentoHackathon\Composer\Magento\Installer
*/
$moduleInstaller = $im->getInstaller("magento-module");
foreach ($installedRepo->getPackages() as $package) {
if ($ioInterface->isVerbose()) {
$ioInterface->write($package->getName());
$ioInterface->write($package->getType());
}
if ($package->getType() != "magento-module") {
continue;
}
if ($ioInterface->isVerbose()) {
$ioInterface->write("package {$package->getName()} recognized");
}
$strategy = $moduleInstaller->getDeployStrategy($package);
if ($ioInterface->getOption('verbose')) {
$ioInterface->write("used " . get_class($strategy) . " as deploy strategy");
}
$strategy->setMappings($moduleInstaller->getParser($package)->getMappings());
$strategy->deploy();
}
return;
}
/**
* Updates specific package
*
* @param InstalledRepositoryInterface $repo repository in which to check
* @param PackageInterface $initial already installed package version
* @param PackageInterface $target updated version
*
* @throws InvalidArgumentException if $from package is not installed
*/
public function update(InstalledRepositoryInterface $repo, PackageInterface $initial, PackageInterface $target)
{
if ($target->getType() === 'magento-core' && !$this->preUpdateMagentoCore()) {
return;
}
// cleanup marshaled files if extra->map exist
if ($this->hasExtraMap($initial)) {
$initialStrategy = $this->getDeployStrategy($initial);
$initialStrategy->setMappings($this->getParser($initial)->getMappings());
try {
$initialStrategy->clean();
} catch (\ErrorException $e) {
if ($this->io->isDebug()) {
$this->io->write($e->getMessage());
}
}
}
parent::update($repo, $initial, $target);
// marshal files for new package version if extra->map exist
if ($this->hasExtraMap($target)) {
$targetStrategy = $this->getDeployStrategy($target);
$targetStrategy->setMappings($this->getParser($target)->getMappings());
$deployManagerEntry = new Entry();
$deployManagerEntry->setPackageName($target->getName());
$deployManagerEntry->setDeployStrategy($targetStrategy);
$this->deployManager->addPackage($deployManagerEntry);
}
if($this->appendGitIgnore) {
$this->appendGitIgnore($target, $this->getGitIgnoreFileLocation());
}
if ($target->getType() === 'magento-core') {
$this->postUpdateMagentoCore();
}
}
protected function preUpdateMagentoCore() {
if (!$this->io->askConfirmation('Are you sure you want to manipulate the Magento core installation [Y,n]? ', true)) {
$this->io->write('Skipping core update...');
return false;
}
$tmpDir = $this->magentoRootDir->getPathname() . self::MAGENTO_ROOT_DIR_TMP_SUFFIX;
$this->filesystem->ensureDirectoryExists($tmpDir);
$this->originalMagentoRootDir = clone $this->magentoRootDir;
$this->magentoRootDir = new \SplFileInfo($tmpDir);
return true;
}
protected function postUpdateMagentoCore() {
$tmpDir = $this->magentoRootDir->getPathname();
$backupDir = $this->originalMagentoRootDir->getPathname() . self::MAGENTO_ROOT_DIR_BACKUP_SUFFIX;
$this->backupMagentoRootDir = new \SplFileInfo($backupDir);
$origRootDir = $this->originalMagentoRootDir->getPathName();
$this->filesystem->rename($origRootDir, $backupDir);
$this->filesystem->rename($tmpDir, $origRootDir);
$this->magentoRootDir = clone $this->originalMagentoRootDir;
$this->prepareMagentoCore();
$this->cleanupPostUpdateMagentoCore();
}
protected function cleanupPostUpdateMagentoCore() {
$rootDir = $this->magentoRootDir->getPathname();
$backupDir = $this->backupMagentoRootDir->getPathname();
$persistentFolders = array('media', 'var');
copy($backupDir . DIRECTORY_SEPARATOR . $this->_magentoLocalXmlPath, $rootDir . DIRECTORY_SEPARATOR . $this->_magentoLocalXmlPath);
foreach ($persistentFolders as $folder) {
$this->filesystem->removeDirectory($rootDir . DIRECTORY_SEPARATOR . $folder);
$this->filesystem->rename($backupDir . DIRECTORY_SEPARATOR . $folder, $rootDir . DIRECTORY_SEPARATOR . $folder);
}
if ($this->io->ask('Remove root backup? [Y,n] ', true)) {
$this->filesystem->removeDirectory($backupDir);
$this->io->write('Removed root backup!', true);
} else {
$this->io->write('Skipping backup removal...', true);
}
$this->clearMagentoCache();
}
public function toggleMagentoMaintenanceMode($active = false) {
if (($targetDir = $this->getTargetDir()) && !$this->noMaintenanceMode) {
$flagPath = $targetDir . DIRECTORY_SEPARATOR . self::MAGENTO_MAINTANANCE_FLAG;
if ($active) {
$this->io->write("Adding magento maintenance flag...");
file_put_contents($flagPath, '*');
} elseif (file_exists($flagPath)) {
$this->io->write("Removing magento maintenance flag...");
unlink($flagPath);
}
}
}
public function clearMagentoCache() {
if (($targetDir = $this->getTargetDir()) && !$this->keepMagentoCache) {
$magentoCachePath = $targetDir . DIRECTORY_SEPARATOR . self::MAGENTO_CACHE_PATH;
if ($this->filesystem->removeDirectory($magentoCachePath)) {
$this->io->write('Magento cache cleared');
}
}
}
/**
* Uninstalls specific package.
*
* @param InstalledRepositoryInterface $repo repository in which to check
* @param PackageInterface $package package instance
*/
public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package)
{
// skip marshal and apply default behavior if extra->map does not exist
if (!$this->hasExtraMap($package)) {
parent::uninstall($repo, $package);
return;
}
$strategy = $this->getDeployStrategy($package);
$strategy->setMappings($this->getParser($package)->getMappings());
try {
$strategy->clean();
} catch (\ErrorException $e) {
if ($this->io->isDebug()) {
$this->io->write($e->getMessage());
}
}
parent::uninstall($repo, $package);
}
/**
* Returns the modman parser for the vendor dir
*
* @param PackageInterface $package
* @return Parser
* @throws \ErrorException
*/
public function getParser(PackageInterface $package)
{
$extra = $package->getExtra();
$moduleSpecificMap = $this->composer->getPackage()->getExtra();
if( isset($moduleSpecificMap['magento-map-overwrite']) ){
$moduleSpecificMap = $this->transformArrayKeysToLowerCase($moduleSpecificMap['magento-map-overwrite']);
if( isset($moduleSpecificMap[$package->getName()]) ){
$map = $moduleSpecificMap[$package->getName()];
}
}
$suffix = PackageTypes::$packageTypes[$package->getType()];
if (isset($map)) {
$parser = new MapParser($map, $this->_pathMappingTranslations,$suffix);
return $parser;
} elseif (isset($extra['map'])) {
$parser = new MapParser($extra['map'], $this->_pathMappingTranslations, $suffix);
return $parser;
} elseif (isset($extra['package-xml'])) {
$parser = new PackageXmlParser($this->getSourceDir($package), $extra['package-xml'], $this->_pathMappingTranslations, $suffix);
return $parser;
} elseif (file_exists($this->getSourceDir($package) . '/modman')) {
$parser = new ModmanParser($this->getSourceDir($package), $this->_pathMappingTranslations, $suffix);
return $parser;
} else {
throw new \ErrorException('Unable to find deploy strategy for module: no known mapping');
}
}
/**
* {@inheritDoc}
*/
public function getInstallPath(PackageInterface $package)
{
if (!is_null($this->modmanRootDir) && true === $this->modmanRootDir->isDir()) {
$targetDir = $package->getTargetDir();
if (!$targetDir) {
list($vendor, $targetDir) = explode('/', $package->getPrettyName());
}
$installPath = $this->modmanRootDir . '/' . $targetDir;
} else {
$installPath = parent::getInstallPath($package);
}
// Make install path absolute. This is needed in the symlink deploy strategies.
if (DIRECTORY_SEPARATOR !== $installPath[0] && $installPath[1] !== ':') {
$installPath = getcwd() . "/$installPath";
}
return $installPath;
}
public function transformArrayKeysToLowerCase($array)
{
$arrayNew = array();
foreach($array as $key=>$value){
$arrayNew[strtolower($key)] = $value;
}
return $arrayNew;
}
/**
* this function is for annoying people with messages.
*
* First usage: get people to vote about the future release of composer so later I can say "you wanted it this way"
*
* @param IOInterface $io
*/
public function annoy(IOInterface $io)
{
/**
* No in future, as some people look for error lines inside of CI Applications, which annoys them
*/
/*
$io->write(' time for voting about the future of the #magento #composer installer. ', true);
$io->write(' https://github.com/magento-hackathon/magento-composer-installer/blob/discussion-master/Milestone/2/index.md ', true);
$io->write(' For the case you don\'t vote, I will ignore your problems till iam finished with the resulting release. ', true);
*
**/
}
/**
* Checks if package has extra map value set
*
* @param PackageInterface $package
* @return bool
*/
private function hasExtraMap(PackageInterface $package) {
$packageExtra = $package->getExtra();
if (isset($packageExtra['map'])) {
return true;
}
return false;
}
}