Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch to using composer create-project #124

Merged
merged 2 commits into from
Sep 1, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 0 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@
],
"require": {
"php": "^7.2.9",
"ext-zip": "*",
"guzzlehttp/guzzle": "^6.0|^7.0",
"symfony/console": "^4.0|^5.0",
"symfony/filesystem": "^4.0|^5.0",
"symfony/process": "^4.2|^5.0"
},
"require-dev": {
Expand Down
193 changes: 46 additions & 147 deletions src/NewCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,13 @@

namespace Laravel\Installer\Console;

use GuzzleHttp\Client;
use RuntimeException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Filesystem\Exception\IOExceptionInterface;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Process\Process;
use ZipArchive;

class NewCommand extends Command
{
Expand All @@ -28,7 +24,6 @@ protected function configure()
->setDescription('Create a new Laravel application')
->addArgument('name', InputArgument::OPTIONAL)
->addOption('dev', null, InputOption::VALUE_NONE, 'Installs the latest "development" release')
->addOption('auth', null, InputOption::VALUE_NONE, 'Installs the Laravel authentication scaffolding')
->addOption('force', 'f', InputOption::VALUE_NONE, 'Forces install even if the directory already exists');
}

Expand All @@ -45,61 +40,28 @@ protected function execute(InputInterface $input, OutputInterface $output)
throw new RuntimeException('The Laravel installer requires PHP 7.3.0 or greater. Please use "composer create-project laravel/laravel" instead.');
}

if (! extension_loaded('zip')) {
throw new RuntimeException('The Zip PHP extension is not installed. Please install it and try again.');
}

$name = $input->getArgument('name');

$directory = $name && $name !== '.' ? getcwd().'/'.$name : getcwd();
$directory = $name && $name !== '.' ? getcwd().'/'.$name : '.';

$version = $this->getVersion($input);

if (! $input->getOption('force')) {
$this->verifyApplicationDoesntExist($directory);
}

$output->writeln('<info>Crafting application...</info>');

$this->download($zipFile = $this->makeFilename(), $this->getVersion($input))
->extract($zipFile, $directory)
->prepareWritableDirectories($directory, $output)
->cleanUp($zipFile);

$composer = $this->findComposer();

$commands = [
$composer.' install --no-scripts',
$composer.' run-script post-root-package-install',
$composer.' run-script post-create-project-cmd',
$composer.' run-script post-autoload-dump',
$composer." create-project laravel/laravel $directory $version --remove-vcs --prefer-dist",
"chmod 644 $directory/artisan",
];

if ($input->getOption('no-ansi')) {
$commands = array_map(function ($value) {
return $value.' --no-ansi';
}, $commands);
if ($directory != '.') {
array_unshift($commands, "rm -rf $directory");
}

if ($input->getOption('quiet')) {
$commands = array_map(function ($value) {
return $value.' --quiet';
}, $commands);
}

$process = Process::fromShellCommandline(implode(' && ', $commands), $directory, null, null, null);

if ('\\' !== DIRECTORY_SEPARATOR && file_exists('/dev/tty') && is_readable('/dev/tty')) {
try {
$process->setTty(true);
} catch (RuntimeException $e) {
$output->writeln('Warning: '.$e->getMessage());
}
}

$process->run(function ($type, $line) use ($output) {
$output->write($line);
});

if ($process->isSuccessful()) {
if ($this->runCommands($commands, $input, $output)->isSuccessful()) {
$output->writeln('<comment>Application ready! Build something amazing.</comment>');
}

Expand All @@ -120,135 +82,72 @@ protected function verifyApplicationDoesntExist($directory)
}

/**
* Generate a random temporary filename.
* Get the version that should be downloaded.
*
* @param \Symfony\Component\Console\Input\InputInterface $input
* @return string
*/
protected function makeFilename()
{
return getcwd().'/laravel_'.md5(time().uniqid()).'.zip';
}

/**
* Download the temporary Zip to the given file.
*
* @param string $zipFile
* @param string $version
* @return $this
*/
protected function download($zipFile, $version = 'master')
{
switch ($version) {
case 'develop':
$filename = 'latest-develop.zip';
break;
case 'auth':
$filename = 'latest-auth.zip';
break;
case 'master':
$filename = 'latest.zip';
break;
}

$response = (new Client)->get('http://cabinet.laravel.com/'.$filename);

file_put_contents($zipFile, $response->getBody());

return $this;
}

/**
* Extract the Zip file into the given directory.
*
* @param string $zipFile
* @param string $directory
* @return $this
*/
protected function extract($zipFile, $directory)
protected function getVersion(InputInterface $input)
{
$archive = new ZipArchive;

$response = $archive->open($zipFile, ZipArchive::CHECKCONS);

if ($response === ZipArchive::ER_NOZIP) {
throw new RuntimeException('The zip file could not download. Verify that you are able to access: http://cabinet.laravel.com/latest.zip');
if ($input->getOption('dev')) {
return 'dev-master';
}

$archive->extractTo($directory);

$archive->close();

return $this;
return '';
}

/**
* Clean-up the Zip file.
*
* @param string $zipFile
* @return $this
*/
protected function cleanUp($zipFile)
{
@chmod($zipFile, 0777);

@unlink($zipFile);

return $this;
}

/**
* Make sure the storage and bootstrap cache directories are writable.
* Get the composer command for the environment.
*
* @param string $appDirectory
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @return $this
* @return string
*/
protected function prepareWritableDirectories($appDirectory, OutputInterface $output)
protected function findComposer()
{
$filesystem = new Filesystem;
$composerPath = getcwd().'/composer.phar';

try {
$filesystem->chmod($appDirectory.DIRECTORY_SEPARATOR.'bootstrap/cache', 0755, 0000, true);
$filesystem->chmod($appDirectory.DIRECTORY_SEPARATOR.'storage', 0755, 0000, true);
} catch (IOExceptionInterface $e) {
$output->writeln('<comment>You should verify that the "storage" and "bootstrap/cache" directories are writable.</comment>');
if (file_exists($composerPath)) {
return '"'.PHP_BINARY.'" '.$composerPath;
}

return $this;
return 'composer';
}

/**
* Get the version that should be downloaded.
* Run the given commands.
*
* @param array $commands
* @param \Symfony\Component\Console\Input\InputInterface $input
* @return string
* @param \Symfony\Component\Console\Output\OutputInterface $output
* @return Process
*/
protected function getVersion(InputInterface $input)
protected function runCommands($commands, InputInterface $input, OutputInterface $output)
{
if ($input->getOption('dev')) {
return 'develop';
if ($input->getOption('no-ansi')) {
$commands = array_map(function ($value) {
return $value.' --no-ansi';
}, $commands);
}

if ($input->getOption('auth')) {
return 'auth';
if ($input->getOption('quiet')) {
$commands = array_map(function ($value) {
return $value.' --quiet';
}, $commands);
}

return 'master';
}

/**
* Get the composer command for the environment.
*
* @return string
*/
protected function findComposer()
{
$composerPath = getcwd().'/composer.phar';
$process = Process::fromShellCommandline(implode(' && ', $commands), null, null, null, null);

if (file_exists($composerPath)) {
return '"'.PHP_BINARY.'" '.$composerPath;
if ('\\' !== DIRECTORY_SEPARATOR && file_exists('/dev/tty') && is_readable('/dev/tty')) {
try {
$process->setTty(true);
} catch (RuntimeException $e) {
$output->writeln('Warning: '.$e->getMessage());
}
}

return 'composer';
$process->run(function ($type, $line) use ($output) {
$output->write($line);
});

return $process;
}
}
8 changes: 2 additions & 6 deletions tests/NewCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Tester\CommandTester;
use Symfony\Component\Filesystem\Filesystem;

class NewCommandTest extends TestCase
{
Expand All @@ -15,20 +14,17 @@ public function test_it_can_scaffold_a_new_laravel_app()
$scaffoldDirectoryName = 'tests-output/my-app';
$scaffoldDirectory = __DIR__.'/../'.$scaffoldDirectoryName;

if (file_exists($scaffoldDirectory)) {
(new Filesystem)->remove($scaffoldDirectory);
}
exec("rm -rf $scaffoldDirectory");

$app = new Application('Laravel Installer');
$app->add(new NewCommand);

$tester = new CommandTester($app->find('new'));

$statusCode = $tester->execute(['name' => $scaffoldDirectoryName, '--auth' => null]);
$statusCode = $tester->execute(['name' => $scaffoldDirectoryName]);

$this->assertEquals($statusCode, 0);
$this->assertDirectoryExists($scaffoldDirectory.'/vendor');
$this->assertFileExists($scaffoldDirectory.'/.env');
$this->assertFileExists($scaffoldDirectory.'/resources/views/auth/login.blade.php');
}
}