diff --git a/doc/create-projects.md b/doc/create-projects.md new file mode 100644 index 000000000..c1e220139 --- /dev/null +++ b/doc/create-projects.md @@ -0,0 +1,22 @@ +# Create Projects + +You can use Composer to create new projects from an existing package. +There are several applications for this: + +1. You can deploy application packages. +2. You can check out any package and start developing on patches for example. +3. Projects with multiple developers can use this feature to bootstrap the initial application for development. + +To create a new project using composer you can use the "create-project", +pass it a package name + version and a directory to create the project in. +The directory is not allowed to exist, it will be created during installation. + + php composer.phar create-project doctrine/orm 2.2.0 /path/to/new-project + +By default the command checks for the packages on packagist.org. To change this behavior +you can use the --repository-url parameter and either point it to an HTTP url +for your own packagist repository or to a packages.json file. + +If you want to get a development version of the code directly checked out +from version control you have to add the --prefer-source parameter. + diff --git a/src/Composer/Command/CreateProjectCommand.php b/src/Composer/Command/CreateProjectCommand.php new file mode 100644 index 000000000..5677a0553 --- /dev/null +++ b/src/Composer/Command/CreateProjectCommand.php @@ -0,0 +1,130 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Command; + +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Input\InputArgument; +use Symfony\Component\Console\Input\ArrayInput; +use Symfony\Component\Console\Output\OutputInterface; +use Composer\IO\IOInterface; +use Composer\Factory; +use Composer\Repository\ComposerRepository; +use Composer\Repository\FilesystemRepository; +use Composer\Installer\ProjectInstaller; + +/** + * Install a package as new project into new directory. + * + * @author Benjamin Eberlei + */ +class CreateProjectCommand extends Command +{ + protected function configure() + { + $this + ->setName('create-project') + ->setDescription('Create new project from a package into given directory.') + ->setDefinition(array( + new InputOption('prefer-source', null, InputOption::VALUE_NONE, 'Forces installation from package sources when possible, including VCS information.'), + new InputOption('repository-url', null, InputOption::VALUE_REQUIRED, 'Pick a different repository url to look for the package.'), + new InputArgument('package', InputArgument::REQUIRED), + new InputArgument('version', InputArgument::OPTIONAL), + new InputArgument('directory', InputArgument::OPTIONAL), + )) + ->setHelp(<<create-project command creates a new project from a given +package into a new directory. You can use this command to bootstrap new +projects or setup a clean version-controlled installation +for developers of your project. + +php composer.phar create-project vendor/project intodirectory + +To setup a developer workable version you should create the project using the source +controlled code by appending the '--prefer-source' flag. + +To install a package from another repository repository than the default one you +can pass the '--repository-url=http://myrepository.org' flag. + +EOT + ) + ; + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $io = $this->getApplication()->getIO(); + + return $this->installProject( + $io, + $this->getInstallCommand($input, $output), + $input->getArgument('package'), + $input->getArgument('directory'), + $input->getArgument('version'), + (Boolean)$input->getOption('prefer-source'), + $input->getOption('repository-url') + ); + } + + protected function getInstallCommand($input, $output) + { + $app = $this->getApplication(); + return function() use ($app, $input, $output) { + $newInput = new ArrayInput(array('command' => 'install')); + $app->doRUn($newInput, $output); + }; + } + + public function installProject(IOInterface $io, $installCommand, $packageName, $directory = null, $version = null, $preferSource = false, $repositoryUrl = null) + { + $dm = $this->createDownloadManager($io); + if ($preferSource) { + $dm->setPreferSource(true); + } + + if (null === $repositoryUrl) { + $sourceRepo = new ComposerRepository(array('url' => 'http://packagist.org')); + } elseif (".json" === substr($repositoryUrl, -5)) { + $sourceRepo = new FilesystemRepository($repositoryUrl); + } elseif (0 === strpos($repositoryUrl, 'http')) { + $sourceRepo = new ComposerRepository(array('url' => $repositoryUrl)); + } else { + throw new \InvalidArgumentException("Invalid repository url given. Has to be a .json file or an http url."); + } + + $package = $sourceRepo->findPackage($packageName, $version); + if (!$package) { + throw new \InvalidArgumentException("Could not find package $packageName with version $version."); + } + + if (null === $directory) { + $parts = explode("/", $packageName); + $directory = getcwd() . DIRECTORY_SEPARATOR . array_pop($parts); + } + + $io->write('Installing ' . $package->getName() . ' as new project.', true); + $projectInstaller = new ProjectInstaller($directory, $dm); + $projectInstaller->install($package); + + $io->write('Created project into directory ' . $directory . '', true); + chdir($directory); + $installCommand(); + } + + protected function createDownloadManager(IOInterface $io) + { + $factory = new Factory(); + return $factory->createDownloadManager($io); + } +} + diff --git a/src/Composer/Console/Application.php b/src/Composer/Console/Application.php index 1881fb575..e836d65c8 100644 --- a/src/Composer/Console/Application.php +++ b/src/Composer/Console/Application.php @@ -107,6 +107,7 @@ class Application extends BaseApplication $this->add(new Command\DependsCommand()); $this->add(new Command\InitCommand()); $this->add(new Command\InstallCommand()); + $this->add(new Command\CreateProjectCommand()); $this->add(new Command\UpdateCommand()); $this->add(new Command\SearchCommand()); $this->add(new Command\ValidateCommand()); @@ -128,4 +129,4 @@ class Application extends BaseApplication return $helperSet; } -} \ No newline at end of file +} diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index faab0add1..4fefb31af 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -139,7 +139,7 @@ class Factory $rm->addRepository(new Repository\ComposerRepository(array('url' => 'http://packagist.org'))); } - protected function createDownloadManager(IOInterface $io) + public function createDownloadManager(IOInterface $io) { $dm = new Downloader\DownloadManager(); $dm->setDownloader('git', new Downloader\GitDownloader($io)); diff --git a/src/Composer/Installer/ProjectInstaller.php b/src/Composer/Installer/ProjectInstaller.php new file mode 100644 index 000000000..da9236a97 --- /dev/null +++ b/src/Composer/Installer/ProjectInstaller.php @@ -0,0 +1,111 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Installer; + +use Composer\DependencyResolver\Operation\OperationInterface; +use Composer\Package\PackageInterface; +use Composer\Downloader\DownloadManager; + +/** + * Project Installer is used to install a single package into a directory as + * root project. + * + * @author Benjamin Eberlei + */ +class ProjectInstaller implements InstallerInterface +{ + private $installPath; + private $downloadManager; + + public function __construct($installPath, DownloadManager $dm) + { + $this->installPath = $installPath; + $this->downloadManager = $dm; + } + + /** + * Decides if the installer supports the given type + * + * @param string $packageType + * @return Boolean + */ + public function supports($packageType) + { + return true; + } + + /** + * Checks that provided package is installed. + * + * @param PackageInterface $package package instance + * + * @return Boolean + */ + public function isInstalled(PackageInterface $package) + { + return false; + } + + /** + * Installs specific package. + * + * @param PackageInterface $package package instance + */ + public function install(PackageInterface $package) + { + $installPath = $this->installPath; + if (file_exists($installPath)) { + throw new \InvalidArgumentException("Project directory $installPath already exists."); + } + if (!file_exists(dirname($installPath))) { + throw new \InvalidArgumentException("Project root " . dirname($installPath) . " does not exist."); + } + mkdir($installPath, 0777); + $this->downloadManager->download($package, $installPath); + } + + /** + * Updates specific package. + * + * @param PackageInterface $initial already installed package version + * @param PackageInterface $target updated version + * + * @throws InvalidArgumentException if $from package is not installed + */ + public function update(PackageInterface $initial, PackageInterface $target) + { + throw new \InvalidArgumentException("not supported"); + } + + /** + * Uninstalls specific package. + * + * @param PackageInterface $package package instance + */ + public function uninstall(PackageInterface $package) + { + throw new \InvalidArgumentException("not supported"); + } + + /** + * Returns the installation path of a package + * + * @param PackageInterface $package + * @return string path + */ + public function getInstallPath(PackageInterface $package) + { + return $this->installPath; + } +} +