Compare commits

..

No commits in common. "df67dafb8ec4e16f71d41db0ad8220fb1e2688a5" and "f3fb68af399647abf668eb21f4f66faa82e2599e" have entirely different histories.

11 changed files with 269 additions and 148 deletions

2
bin/spinner Executable file → Normal file
View file

@ -1,7 +1,6 @@
#!/usr/bin/env php #!/usr/bin/env php
<?php <?php
use Loom\Spinner\Command\DestroyCommand;
use Loom\Spinner\Command\SpinCommand; use Loom\Spinner\Command\SpinCommand;
use Symfony\Component\Console\Application; use Symfony\Component\Console\Application;
@ -10,7 +9,6 @@ require dirname(__DIR__) . '/vendor/autoload.php';
$application = new Application('Loom Spinner'); $application = new Application('Loom Spinner');
$application->add(new SpinCommand()); $application->add(new SpinCommand());
$application->add(new DestroyCommand());
try { try {
$application->run(); $application->run();

View file

@ -4,52 +4,41 @@ declare(strict_types=1);
namespace Loom\Spinner\Classes\Config; namespace Loom\Spinner\Classes\Config;
use Loom\Spinner\Classes\Collection\FilePathCollection;
use Loom\Spinner\Classes\File\Interface\DataPathInterface;
use Loom\Spinner\Classes\File\SpinnerFilePath;
use Loom\Utility\FilePath\FilePath;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Yaml\Yaml; use Symfony\Component\Yaml\Yaml;
class Config class Config
{ {
private string $spinnerRootPath; private FilePathCollection $filePaths;
private string $configDirectory;
private string $dataDirectory;
private ?string $projectWorkPath = null;
public function __construct(string $projectName, ?string $projectWorkPath = null) public function __construct()
{ {
$this->spinnerRootPath = dirname(__DIR__, 3); $this->setFilePaths();
$this->configDirectory = $this->spinnerRootPath . '/config';
$this->dataDirectory = $this->spinnerRootPath . '/data/environments/' . $projectName;
if ($projectWorkPath) {
$this->projectWorkPath = $projectWorkPath;
}
} }
public function getDataDirectory(): string public function getFilePaths(): FilePathCollection
{ {
return $this->dataDirectory; return $this->filePaths;
} }
public function getConfigFilePath(string $fileName): string public function getFilePath(string $key): ?FilePath
{ {
return $this->configDirectory . '/' . $fileName; return $this->filePaths->get($key);
} }
public function getConfigFileContents(string $fileName): string|null public function addFilePath(FilePath $filePath, string $key): void
{ {
if (file_exists($this->configDirectory . '/' . $fileName)) { $this->filePaths->add($filePath, $key);
return file_get_contents($this->configDirectory . '/' . $fileName);
}
return null;
} }
public function projectDataExists(string $projectName): bool /**
{ * @throws \Exception
return file_exists($this->dataDirectory . '/environments/' . $projectName); */
} public function getPhpVersion(InputInterface $input): ?float
public function getPhpVersion(InputInterface $input): float
{ {
if ($input->getOption('php')) { if ($input->getOption('php')) {
return (float) $input->getOption('php'); return (float) $input->getOption('php');
@ -103,7 +92,7 @@ class Config
return false; return false;
} }
return (bool) $this->getEnvironmentOption('database', 'enabled'); return $this->getEnvironmentOption('database', 'enabled');
} }
/** /**
@ -130,29 +119,52 @@ class Config
return (int) $this->getEnvironmentOption('node','version'); return (int) $this->getEnvironmentOption('node','version');
} }
/**
* @throws \Exception
*/
public function getEnvironmentOption(string $service, string $option): mixed public function getEnvironmentOption(string $service, string $option): mixed
{ {
$projectCustomConfig = $this->getProjectCustomConfig(); $projectCustomConfig = $this->filePaths->get('projectCustomConfig');
if ($projectCustomConfig) { if ($projectCustomConfig->exists()) {
return $projectCustomConfig[$service][$option] ?? $this->getDefaultConfig()[$service][$option] ?? null; $customConfig = Yaml::parseFile($projectCustomConfig->getAbsolutePath())['options']['environment']
?? null;
if ($customConfig) {
if (isset($customConfig[$service][$option])) {
return $customConfig[$service][$option];
}
}
} }
return $this->getDefaultConfig()[$service][$option] ?? null; return $this->getDefaultConfig()['environment'][$service][$option] ?? null;
}
public function getProjectCustomConfig(): ?array
{
if ($this->projectWorkPath && file_exists($this->projectWorkPath . '/spinner.yaml')) {
return Yaml::parseFile($this->projectWorkPath . '/spinner.yaml')['options']['environment'];
}
return null;
} }
/**
* @throws \Exception
*/
protected function getDefaultConfig(): ?array protected function getDefaultConfig(): ?array
{ {
return Yaml::parseFile($this->configDirectory . '/spinner.yaml')['options']['environment'] return Yaml::parseFile($this->filePaths->get('defaultSpinnerConfig')?->getAbsolutePath())['options']
?? null; ?? null;
} }
private function setFilePaths(): void
{
$this->filePaths = new FilePathCollection([
'config' => new SpinnerFilePath('config'),
'defaultSpinnerConfig' => new SpinnerFilePath('config/spinner.yaml'),
'envTemplate' => new SpinnerFilePath('config/.template.env'),
'data' => new SpinnerFilePath('data'),
'phpYamlTemplate' => new SpinnerFilePath('config/php.yaml'),
'nginxYamlTemplate' => new SpinnerFilePath('config/nginx.yaml'),
'phpFpmDataDirectory' => new SpinnerFilePath('config/php-fpm'),
DataPathInterface::CONFIG_PHP_FPM_DOCKERFILE => new SpinnerFilePath(DataPathInterface::CONFIG_PHP_FPM_DOCKERFILE),
DataPathInterface::CONFIG_NGINX_DOCKERFILE => new SpinnerFilePath(DataPathInterface::CONFIG_NGINX_DOCKERFILE),
'nodeDockerfileTemplate' => new SpinnerFilePath('config/php-fpm/Node.Dockerfile'),
'xdebugIniTemplate' => new SpinnerFilePath('config/php-fpm/xdebug.ini'),
'opcacheIniTemplate' => new SpinnerFilePath('config/php-fpm/opcache.ini'),
'xdebugDockerfileTemplate' => new SpinnerFilePath('config/php-fpm/XDebug.Dockerfile'),
]);
}
} }

View file

@ -12,7 +12,7 @@ abstract class AbstractFileBuilder implements FileBuilderInterface
{ {
protected string $content; protected string $content;
public function __construct(protected string $path, protected Config $config) public function __construct(protected SpinnerFilePath $path, protected Config $config)
{ {
return $this; return $this;
} }
@ -21,7 +21,7 @@ abstract class AbstractFileBuilder implements FileBuilderInterface
public function save(): void public function save(): void
{ {
file_put_contents($this->path, $this->content); file_put_contents($this->path->getProvidedPath(), $this->content);
} }

View file

@ -5,7 +5,6 @@ declare(strict_types=1);
namespace Loom\Spinner\Classes\File; namespace Loom\Spinner\Classes\File;
use Loom\Spinner\Classes\Config\Config; use Loom\Spinner\Classes\Config\Config;
use Loom\Utility\FilePath\FilePath;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
class DockerComposeFileBuilder extends AbstractFileBuilder class DockerComposeFileBuilder extends AbstractFileBuilder
@ -15,7 +14,13 @@ class DockerComposeFileBuilder extends AbstractFileBuilder
*/ */
public function __construct(Config $config) public function __construct(Config $config)
{ {
return parent::__construct($config->getDataDirectory() . '/docker-compose.yaml', $config); $projectDockerCompose = $config->getFilePaths()->get('projectDockerCompose');
if (!$projectDockerCompose instanceof SpinnerFilePath) {
throw new \Exception('Project Docker Compose file path not found.');
}
return parent::__construct($projectDockerCompose, $config);
} }
/** /**
@ -23,7 +28,9 @@ class DockerComposeFileBuilder extends AbstractFileBuilder
*/ */
public function build(InputInterface $input): DockerComposeFileBuilder public function build(InputInterface $input): DockerComposeFileBuilder
{ {
$this->content = $this->config->getConfigFileContents('php.yaml'); $this->content = file_get_contents(
$this->config->getFilePaths()->get('phpYamlTemplate')->getAbsolutePath()
);
if ($this->config->isDatabaseEnabled($input) && in_array($this->config->getDatabaseDriver($input), ['sqlite3', 'sqlite'])) { if ($this->config->isDatabaseEnabled($input) && in_array($this->config->getDatabaseDriver($input), ['sqlite3', 'sqlite'])) {
$this->addSqliteDatabaseConfig(); $this->addSqliteDatabaseConfig();
@ -41,18 +48,20 @@ class DockerComposeFileBuilder extends AbstractFileBuilder
$this->content .= str_replace( $this->content .= str_replace(
'services:', 'services:',
'', '',
$this->config->getConfigFileContents('nginx.yaml') file_get_contents(
$this->config->getFilePaths()->get('nginxYamlTemplate')->getAbsolutePath()
)
); );
$this->content = str_replace( $this->content = str_replace(
'./nginx/conf.d', './nginx/conf.d',
$this->config->getConfigFilePath('nginx/conf.d'), (new SpinnerFilePath('config/nginx/conf.d'))->getAbsolutePath(),
$this->content $this->content
); );
} }
private function addSqliteDatabaseConfig(): void private function addSqliteDatabaseConfig(): void
{ {
$sqlLiteConfig = $this->config->getConfigFileContents('sqlite.yaml'); $sqlLiteConfig = file_get_contents((new SpinnerFilePath('config/sqlite.yaml'))->getAbsolutePath());
$sqlLiteConfig = str_replace('volumes:', '', $sqlLiteConfig); $sqlLiteConfig = str_replace('volumes:', '', $sqlLiteConfig);
$this->content .= $sqlLiteConfig; $this->content .= $sqlLiteConfig;
} }

View file

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Loom\Spinner\Classes\File\Interface;
interface DataPathInterface
{
public const string CONFIG_PHP_FPM_DOCKERFILE = 'config/php-fpm/Dockerfile';
public const string CONFIG_NGINX_DOCKERFILE = 'config/nginx/Dockerfile';
}

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Loom\Spinner\Classes\File; namespace Loom\Spinner\Classes\File;
use Loom\Spinner\Classes\Config\Config; use Loom\Spinner\Classes\Config\Config;
use Loom\Spinner\Classes\File\Interface\DataPathInterface;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
class NginxDockerFileBuilder extends AbstractFileBuilder class NginxDockerFileBuilder extends AbstractFileBuilder
@ -14,12 +15,22 @@ class NginxDockerFileBuilder extends AbstractFileBuilder
*/ */
public function __construct(Config $config) public function __construct(Config $config)
{ {
return parent::__construct($config->getDataDirectory() . '/nginx/Dockerfile', $config); $projectNginxDockerfilePath = $config->getFilePath('projectNginxDockerfile');
if (!$projectNginxDockerfilePath instanceof SpinnerFilePath) {
throw new \Exception('Project PHP-FPM Dockerfile not found');
}
return parent::__construct($projectNginxDockerfilePath, $config);
} }
public function build(InputInterface $input): AbstractFileBuilder public function build(InputInterface $input): AbstractFileBuilder
{ {
$this->content = $this->config->getConfigFileContents('nginx/Dockerfile'); $this->content = file_get_contents(
$this->config->getFilePaths()
->get(DataPathInterface::CONFIG_NGINX_DOCKERFILE)
->getAbsolutePath()
);
return $this; return $this;
} }

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Loom\Spinner\Classes\File; namespace Loom\Spinner\Classes\File;
use Loom\Spinner\Classes\Config\Config; use Loom\Spinner\Classes\Config\Config;
use Loom\Spinner\Classes\File\Interface\DataPathInterface;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
class PHPDockerFileBuilder extends AbstractFileBuilder class PHPDockerFileBuilder extends AbstractFileBuilder
@ -14,7 +15,13 @@ class PHPDockerFileBuilder extends AbstractFileBuilder
*/ */
public function __construct(Config $config) public function __construct(Config $config)
{ {
return parent::__construct($config->getDataDirectory() . '/php-fpm/Dockerfile', $config); $projectPhpFpmDockerfile = $config->getFilePath('projectPhpFpmDockerfile');
if (!$projectPhpFpmDockerfile instanceof SpinnerFilePath) {
throw new \Exception('Project PHP-FPM Dockerfile not found');
}
return parent::__construct($projectPhpFpmDockerfile, $config);
} }
/** /**
@ -27,27 +34,27 @@ class PHPDockerFileBuilder extends AbstractFileBuilder
$this->content = str_replace('${PHP_VERSION}', (string) $this->config->getPhpVersion($input), $this->content); $this->content = str_replace('${PHP_VERSION}', (string) $this->config->getPhpVersion($input), $this->content);
file_put_contents( file_put_contents(
$this->config->getDataDirectory() . '/php-fpm/opcache.ini', (new SpinnerFilePath(sprintf('data/environments/%s/php-fpm/opcache.ini', $input->getArgument('name'))))->getProvidedPath(),
$this->config->getConfigFileContents('php-fpm/opcache.ini') file_get_contents($this->config->getFilePaths()->get('opcacheIniTemplate')->getAbsolutePath())
); );
if ($this->config->isDatabaseEnabled($input) && in_array($this->config->getDatabaseDriver($input), ['sqlite3', 'sqlite'])) { if ($this->config->isDatabaseEnabled($input) && in_array($this->config->getDatabaseDriver($input), ['sqlite3', 'sqlite'])) {
$this->addNewLine(); $this->addNewLine();
$this->content .= $this->config->getConfigFileContents('php-fpm/Sqlite.Dockerfile'); $this->content .= file_get_contents((new SpinnerFilePath('config/php-fpm/Sqlite.Dockerfile'))->getAbsolutePath());
} }
if ($this->config->isNodeEnabled($input)) { if ($this->config->isNodeEnabled($input)) {
$this->addNewLine(); $this->addNewLine();
$this->content .= $this->config->getConfigFileContents('php-fpm/Node.Dockerfile'); $this->content .= file_get_contents($this->config->getFilePaths()->get('nodeDockerfileTemplate')->getAbsolutePath());
$this->content = str_replace('${NODE_VERSION}', (string) $this->config->getNodeVersion($input), $this->content); $this->content = str_replace('${NODE_VERSION}', (string) $this->config->getNodeVersion($input), $this->content);
} }
if ($this->config->isXdebugEnabled($input)) { if ($this->config->isXdebugEnabled($input)) {
$this->addNewLine(); $this->addNewLine();
$this->content .= $this->config->getConfigFileContents('php-fpm/Xdebug.Dockerfile'); $this->content .= file_get_contents($this->config->getFilePaths()->get('xdebugDockerfileTemplate')->getAbsolutePath());
file_put_contents( file_put_contents(
$this->config->getDataDirectory() . '/php-fpm/xdebug.ini', (new SpinnerFilePath(sprintf('data/environments/%s/php-fpm/xdebug.ini', $input->getArgument('name'))))->getProvidedPath(),
$this->config->getConfigFileContents('php-fpm/xdebug.ini') file_get_contents($this->config->getFilePaths()->get('xdebugIniTemplate')->getAbsolutePath())
); );
} }
@ -56,6 +63,8 @@ class PHPDockerFileBuilder extends AbstractFileBuilder
private function setInitialContent(): void private function setInitialContent(): void
{ {
$this->content = $this->config->getConfigFileContents('php-fpm/Dockerfile'); $this->content = file_get_contents(
$this->config->getFilePaths()->get(DataPathInterface::CONFIG_PHP_FPM_DOCKERFILE)->getAbsolutePath()
);
} }
} }

View file

@ -0,0 +1,20 @@
<?php
declare(strict_types=1);
namespace Loom\Spinner\Classes\File;
use Loom\Utility\FilePath\FilePath;
class SpinnerFilePath extends FilePath
{
public function __construct(string $path)
{
parent::__construct(sprintf('%s%s%s', dirname(__DIR__, 3), DIRECTORY_SEPARATOR, $path));
}
public function getProvidedPath(): string
{
return $this->path;
}
}

View file

@ -4,14 +4,17 @@ declare(strict_types=1);
namespace Loom\Spinner\Command; namespace Loom\Spinner\Command;
use Loom\Spinner\Classes\Collection\FilePathCollection;
use Loom\Spinner\Classes\Config\Config; use Loom\Spinner\Classes\Config\Config;
use Loom\Spinner\Classes\File\SpinnerFilePath;
use Loom\Spinner\Classes\OS\System; use Loom\Spinner\Classes\OS\System;
use Loom\Spinner\Command\Interface\ConsoleCommandInterface; use Loom\Spinner\Command\Interface\ConsoleCommandInterface;
use Loom\Utility\FilePath\FilePath;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle; use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Yaml\Yaml;
class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface
{ {
@ -22,6 +25,7 @@ class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface
public function __construct() public function __construct()
{ {
$this->system = new System(); $this->system = new System();
$this->config = new Config();
parent::__construct(); parent::__construct();
} }
@ -38,20 +42,73 @@ class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface
return Command::FAILURE; return Command::FAILURE;
} }
if (!$this->validatePathArgument($input)) {
return Command::FAILURE;
}
$this->validateNameArgument($input);
return Command::SUCCESS; return Command::SUCCESS;
} }
protected function buildDockerComposeCommand(string $command, bool $daemon = true): string protected function buildDockerComposeCommand(string $command, bool $daemon = true): string
{ {
return sprintf( return sprintf(
'cd %s && docker compose --env-file=%s %s%s', 'cd %s && docker-compose --env-file=%s %s%s',
$this->config->getDataDirectory(), $this->config->getFilePaths()->get('projectData')->getAbsolutePath(),
$this->config->getDataDirectory() . '/.env', $this->config->getFilePaths()->get('projectEnv')->getAbsolutePath(),
$command, $command,
$daemon ? ' -d' : '' $daemon ? ' -d' : ''
); );
} }
private function validatePathArgument(InputInterface $input): bool
{
if ($input->hasArgument('path')) {
$projectDirectory = new FilePath($input->getArgument('path'));
if (!$projectDirectory->exists() || !$projectDirectory->isDirectory()) {
$this->style->error('The provided path is not a valid directory.');
return false;
}
$this->config->addFilePath($projectDirectory, 'project');
}
return true;
}
private function validateNameArgument(InputInterface $input): void
{
if ($input->hasArgument('name')) {
$this->config->addFilePath(
new SpinnerFilePath(sprintf('data/environments/%s', $input->getArgument('name'))),
'projectData'
);
$this->config->addFilePath(
new SpinnerFilePath(sprintf('data/environments/%s/.env', $input->getArgument('name'))),
'projectEnv'
);
$this->config->addFilePath(
new SpinnerFilePath(sprintf('data/environments/%s/docker-compose.yml', $input->getArgument('name'))),
'projectDockerCompose'
);
$this->config->addFilePath(
new SpinnerFilePath(sprintf('data/environments/%s/php-fpm/Dockerfile', $input->getArgument('name'))),
'projectPhpFpmDockerfile'
);
$this->config->addFilePath(
new SpinnerFilePath(sprintf('data/environments/%s/nginx/Dockerfile', $input->getArgument('name'))),
'projectNginxDockerfile'
);
$this->config->addFilePath(
new FilePath($this->config->getFilePaths()->get('project')->getAbsolutePath() . DIRECTORY_SEPARATOR . 'spinner.yaml'),
'projectCustomConfig'
);
}
}
private function setStyle(InputInterface $input, OutputInterface $output): void private function setStyle(InputInterface $input, OutputInterface $output): void
{ {
$this->style = new SymfonyStyle($input, $output); $this->style = new SymfonyStyle($input, $output);

View file

@ -1,33 +0,0 @@
<?php
declare(strict_types=1);
namespace Loom\Spinner\Command;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
#[AsCommand(name: 'spin:down', description: 'Destroy a development environment')]
class DestroyCommand extends AbstractSpinnerCommand
{
protected function configure(): void
{
$this->addArgument(
'name',
InputArgument::REQUIRED,
'The name of the project (as used when running spin:up).'
);
}
protected function execute(InputInterface $input, OutputInterface $output): int
{
if (parent::execute($input, $output)) {
return Command::FAILURE;
}
return Command::SUCCESS;
}
}

View file

@ -4,10 +4,10 @@ declare(strict_types=1);
namespace Loom\Spinner\Command; namespace Loom\Spinner\Command;
use Loom\Spinner\Classes\Config\Config;
use Loom\Spinner\Classes\File\DockerComposeFileBuilder; use Loom\Spinner\Classes\File\DockerComposeFileBuilder;
use Loom\Spinner\Classes\File\NginxDockerFileBuilder; use Loom\Spinner\Classes\File\NginxDockerFileBuilder;
use Loom\Spinner\Classes\File\PHPDockerFileBuilder; use Loom\Spinner\Classes\File\PHPDockerFileBuilder;
use Loom\Spinner\Classes\File\SpinnerFilePath;
use Loom\Spinner\Classes\OS\PortGenerator; use Loom\Spinner\Classes\OS\PortGenerator;
use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
@ -21,7 +21,6 @@ class SpinCommand extends AbstractSpinnerCommand
{ {
private PortGenerator $portGenerator; private PortGenerator $portGenerator;
private array $ports; private array $ports;
private string $projectWorkPath = '';
public function __construct() public function __construct()
{ {
@ -85,35 +84,14 @@ class SpinCommand extends AbstractSpinnerCommand
return Command::FAILURE; return Command::FAILURE;
} }
$this->projectWorkPath = $input->getArgument('path') === '.' if ($this->projectDataExists()) {
? getcwd() return Command::SUCCESS;
: $input->getArgument('path');
if (!file_exists($this->projectWorkPath)) {
$this->style->error('The provided project path does not exist.');
return Command::FAILURE;
}
$this->config = new Config($input->getArgument('name'), $this->projectWorkPath);
if ($this->config->projectDataExists($input->getArgument('name'))) {
$this->style->error('A project with the same name already exists.');
return Command::FAILURE;
} }
$this->style->success("Spinning up a new development environment..."); $this->style->success("Spinning up a new development environment...");
$this->style->text('Creating project data...'); $this->style->text('Creating project data...');
$this->createProjectData($input);
try {
$this->createProjectData($input);
} catch (\Exception $exception) {
$this->style->error('Failed to create project data: '. $exception->getMessage());
return Command::FAILURE;
}
$this->style->success('Project data created.'); $this->style->success('Project data created.');
$this->style->text('Building Docker images...'); $this->style->text('Building Docker images...');
@ -124,12 +102,23 @@ class SpinCommand extends AbstractSpinnerCommand
return Command::SUCCESS; return Command::SUCCESS;
} }
protected function projectDataExists(): bool
{
if ($this->config->getFilePaths()->get('projectData')->exists()) {
$this->style->warning('Project already exists. Skipping new build.');
return true;
}
return false;
}
/** /**
* @throws \Exception * @throws \Exception
*/ */
private function createProjectData(InputInterface $input): void private function createProjectData(InputInterface $input): void
{ {
$this->createProjectDataDirectory($input); $this->createProjectDataDirectory();
$this->createEnvironmentFile($input); $this->createEnvironmentFile($input);
$this->buildDockerComposeFile($input); $this->buildDockerComposeFile($input);
$this->buildDockerfiles($input); $this->buildDockerfiles($input);
@ -138,13 +127,15 @@ class SpinCommand extends AbstractSpinnerCommand
/** /**
* @throws \Exception * @throws \Exception
*/ */
private function createProjectDataDirectory(InputInterface $input): void private function createProjectDataDirectory(): void
{ {
mkdir( $projectData = $this->config->getFilePaths()->get('projectData');
$this->config->getDataDirectory(),
0777, if (!$projectData instanceof SpinnerFilePath) {
true throw new \Exception('Invalid project data directory provided.');
); }
mkdir($projectData->getProvidedPath(), 0777, true);
} }
/** /**
@ -152,15 +143,21 @@ class SpinCommand extends AbstractSpinnerCommand
*/ */
private function createEnvironmentFile(InputInterface $input): void private function createEnvironmentFile(InputInterface $input): void
{ {
$projectEnv = $this->config->getFilePaths()->get('projectEnv');
if (!$projectEnv instanceof SpinnerFilePath) {
throw new \Exception('Invalid project environment file provided.');
}
file_put_contents( file_put_contents(
$this->config->getDataDirectory() . '/.env', $projectEnv->getProvidedPath(),
sprintf( sprintf(
$this->config->getConfigFileContents('.template.env'), file_get_contents($this->config->getFilePaths()->get('envTemplate')->getAbsolutePath()),
$this->projectWorkPath, $this->config->getFilePaths()->get('project')->getAbsolutePath(),
$input->getArgument('name'), $input->getArgument('name'),
$this->config->getPhpVersion($input), $this->config->getPhpVersion($input),
$this->ports['php'], $this->getPort('php'),
$this->ports['nginx'], $this->getPort('nginx'),
) )
); );
} }
@ -170,7 +167,12 @@ class SpinCommand extends AbstractSpinnerCommand
*/ */
private function buildDockerComposeFile(InputInterface $input): void private function buildDockerComposeFile(InputInterface $input): void
{ {
$this->createProjectDataSubDirectory('php-fpm'); $this->createProjectPhpFpmDirectory();
if ($this->config->isServerEnabled($input)) {
$this->createProjectNginxDirectory();
(new NginxDockerFileBuilder($this->config))->build($input)->save();
}
(new DockerComposeFileBuilder($this->config))->build($input)->save(); (new DockerComposeFileBuilder($this->config))->build($input)->save();
} }
@ -180,18 +182,43 @@ class SpinCommand extends AbstractSpinnerCommand
*/ */
private function buildDockerfiles(InputInterface $input): void private function buildDockerfiles(InputInterface $input): void
{ {
if ($this->config->isServerEnabled($input)) {
$this->createProjectDataSubDirectory('nginx');
(new NginxDockerFileBuilder($this->config))->build($input)->save();
}
(new PHPDockerFileBuilder($this->config))->build($input)->save(); (new PHPDockerFileBuilder($this->config))->build($input)->save();
} }
private function createProjectDataSubDirectory(string $directory): void /**
* @throws \Exception
*/
private function createProjectPhpFpmDirectory(): void
{ {
if (!file_exists($this->config->getDataDirectory() . '/' . $directory)) { $projectData = $this->config->getFilePaths()->get('projectData');
mkdir($this->config->getDataDirectory() . '/' . $directory, 0777, true);
if (!$projectData instanceof SpinnerFilePath) {
throw new \Exception('Invalid project data directory provided.');
}
if (!file_exists($projectData->getProvidedPath() . '/php-fpm')) {
mkdir($projectData->getProvidedPath() . '/php-fpm', 0777, true);
} }
} }
/**
* @throws \Exception
*/
private function createProjectNginxDirectory(): void
{
$projectData = $this->config->getFilePaths()->get('projectData');
if (!$projectData instanceof SpinnerFilePath) {
throw new \Exception('Invalid project data directory provided.');
}
if (!file_exists($projectData->getProvidedPath() . '/nginx')) {
mkdir($projectData->getProvidedPath() . '/nginx', 0777, true);
}
}
private function getPort(string $service): ?int
{
return $this->ports[$service];
}
} }