Create basic project data structure from env template
This commit is contained in:
parent
940fbc1234
commit
b8ad06cda3
11 changed files with 179 additions and 46 deletions
|
@ -13,7 +13,8 @@
|
||||||
"require": {
|
"require": {
|
||||||
"loomlabs/utility.collection": "^1.0",
|
"loomlabs/utility.collection": "^1.0",
|
||||||
"loomlabs/utility.filepath": "^1.1",
|
"loomlabs/utility.filepath": "^1.1",
|
||||||
"symfony/console": "^7.2"
|
"symfony/console": "^7.2",
|
||||||
|
"symfony/yaml": "^7.2"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^12.1"
|
"phpunit/phpunit": "^12.1"
|
||||||
|
|
3
config/.template.env
Normal file
3
config/.template.env
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
PROJECT_DIRECTORY=%s
|
||||||
|
PROJECT_NAME=%s
|
||||||
|
PHP_VERSION=%s
|
26
config/docker-compose.sample.yaml
Normal file
26
config/docker-compose.sample.yaml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
services:
|
||||||
|
# nginx:
|
||||||
|
# build:
|
||||||
|
# context: ./nginx
|
||||||
|
# ports:
|
||||||
|
# - ${NGINX_PORT}:80
|
||||||
|
# volumes:
|
||||||
|
# - ${PROJECT_DIRECTORY}:/var/www/html:cached
|
||||||
|
# - ./nginx/conf.d:/etc/nginx/conf.d
|
||||||
|
# database:
|
||||||
|
# image: mysql:8.0
|
||||||
|
# command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
|
||||||
|
# ports:
|
||||||
|
# - ${MYSQL_PORT}:3306
|
||||||
|
# expose:
|
||||||
|
# - "3306"
|
||||||
|
# environment:
|
||||||
|
# MYSQL_ROOT_PASSWORD: docker
|
||||||
|
# volumes:
|
||||||
|
# - ./data/${PROJECT_NAME}/mysql:/var/lib/mysql:cached
|
||||||
|
# cache:
|
||||||
|
# image: redis:latest
|
||||||
|
# ports:
|
||||||
|
# - ${REDIS_PORT}:6379
|
||||||
|
# volumes:
|
||||||
|
# - ./data/${PROJECT_NAME}/redis:/data:cached
|
|
@ -1,38 +0,0 @@
|
||||||
version: "3.9"
|
|
||||||
|
|
||||||
services:
|
|
||||||
nginx:
|
|
||||||
build:
|
|
||||||
context: ./nginx
|
|
||||||
ports:
|
|
||||||
- ${NGINX_PORT}:80
|
|
||||||
volumes:
|
|
||||||
- ${PROJECT_DIRECTORY}:/var/www/html:cached
|
|
||||||
- ./nginx/conf.d:/etc/nginx/conf.d
|
|
||||||
php:
|
|
||||||
build:
|
|
||||||
context: ./php-fpm
|
|
||||||
working_dir: /var/www/html
|
|
||||||
extra_hosts:
|
|
||||||
- host.docker.internal:host-gateway
|
|
||||||
volumes:
|
|
||||||
- ${PROJECT_DIRECTORY}:/var/www/html:cached
|
|
||||||
ports:
|
|
||||||
- ${PHP_PORT}:9003
|
|
||||||
database:
|
|
||||||
image: mysql:8.0
|
|
||||||
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
|
|
||||||
ports:
|
|
||||||
- ${MYSQL_PORT}:3306
|
|
||||||
expose:
|
|
||||||
- "3306"
|
|
||||||
environment:
|
|
||||||
MYSQL_ROOT_PASSWORD: docker
|
|
||||||
volumes:
|
|
||||||
- ./data/${PROJECT_NAME}/mysql:/var/lib/mysql:cached
|
|
||||||
cache:
|
|
||||||
image: redis:latest
|
|
||||||
ports:
|
|
||||||
- ${REDIS_PORT}:6379
|
|
||||||
volumes:
|
|
||||||
- ./data/${PROJECT_NAME}/redis:/data:cached
|
|
41
config/php-fpm/Dockerfile
Normal file
41
config/php-fpm/Dockerfile
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
ARG PHP_VERSION
|
||||||
|
|
||||||
|
ENV PHP_VERSION ${PHP_VERSION}
|
||||||
|
|
||||||
|
FROM php:${PHP_VERSION}-fpm
|
||||||
|
|
||||||
|
RUN mkdir -p /var/www/html
|
||||||
|
|
||||||
|
RUN apt-get -qq update && apt-get -qq install -y \
|
||||||
|
apt-transport-https \
|
||||||
|
git \
|
||||||
|
curl \
|
||||||
|
zip \
|
||||||
|
unzip \
|
||||||
|
libicu-dev \
|
||||||
|
imagick \
|
||||||
|
nano \
|
||||||
|
bash \
|
||||||
|
dnsutils
|
||||||
|
|
||||||
|
ENV NVM_DIR /root/.nvm
|
||||||
|
ENV NODE_VERSION 20
|
||||||
|
|
||||||
|
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash \
|
||||||
|
&& . "$NVM_DIR/nvm.sh" \
|
||||||
|
&& nvm install $NODE_VERSION \
|
||||||
|
&& nvm use $NODE_VERSION
|
||||||
|
|
||||||
|
COPY --from=node:20 /usr/local/bin/npx /usr/local/bin/npx
|
||||||
|
|
||||||
|
RUN docker-php-ext-configure intl > /dev/null
|
||||||
|
RUN docker-php-ext-install mysqli pdo pdo_mysql sockets intl exif bcmath opcache > /dev/null
|
||||||
|
|
||||||
|
RUN pecl install xdebug redis \
|
||||||
|
&& docker-php-ext-enable redis
|
||||||
|
|
||||||
|
COPY ./xdebug.ini.tmp "${PHP_INI_DIR}/conf.d/xdebug.ini"
|
||||||
|
COPY ./opcache.ini "${PHP_INI_DIR}/conf.d"
|
||||||
|
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
|
||||||
|
|
||||||
|
WORKDIR /var/www/html
|
10
config/php.yaml
Normal file
10
config/php.yaml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
php:
|
||||||
|
build:
|
||||||
|
context: ./php-fpm
|
||||||
|
working_dir: /var/www/html
|
||||||
|
extra_hosts:
|
||||||
|
- host.docker.internal:host-gateway
|
||||||
|
volumes:
|
||||||
|
- ${PROJECT_DIRECTORY}:/var/www/html:cached
|
||||||
|
ports:
|
||||||
|
- ${PHP_PORT}:9003
|
4
config/spinner.yaml
Normal file
4
config/spinner.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
options:
|
||||||
|
environment:
|
||||||
|
php:
|
||||||
|
version: 8.4
|
2
data/.gitignore
vendored
Normal file
2
data/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
|
@ -8,8 +8,13 @@ use Loom\Utility\FilePath\FilePath;
|
||||||
|
|
||||||
class SpinnerFilePath extends FilePath
|
class SpinnerFilePath extends FilePath
|
||||||
{
|
{
|
||||||
public function __construct(string $projectPath)
|
public function __construct(string $path)
|
||||||
{
|
{
|
||||||
parent::__construct(sprintf('%s%s%s', dirname(__DIR__, 3), DIRECTORY_SEPARATOR, $projectPath));
|
parent::__construct(sprintf('%s%s%s', dirname(__DIR__, 3), DIRECTORY_SEPARATOR, $path));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProvidedPath(): string
|
||||||
|
{
|
||||||
|
return $this->path;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,11 +8,11 @@ use Loom\Spinner\Classes\Collection\FilePathCollection;
|
||||||
use Loom\Spinner\Classes\File\SpinnerFilePath;
|
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\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
|
||||||
{
|
{
|
||||||
|
@ -34,6 +34,8 @@ class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface
|
||||||
{
|
{
|
||||||
$this->setStyle($input, $output);
|
$this->setStyle($input, $output);
|
||||||
|
|
||||||
|
$this->style->title('Loom Spinner');
|
||||||
|
|
||||||
if (!$this->system->isDockerEngineRunning()) {
|
if (!$this->system->isDockerEngineRunning()) {
|
||||||
$this->style->error('It looks like the Docker Engine is not running. Please start it and try again.');
|
$this->style->error('It looks like the Docker Engine is not running. Please start it and try again.');
|
||||||
|
|
||||||
|
@ -41,7 +43,7 @@ class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($input->hasArgument('path')) {
|
if ($input->hasArgument('path')) {
|
||||||
$projectDirectory = new FilePath($input->getArgument('path'));
|
$projectDirectory = new SpinnerFilePath($input->getArgument('path'));
|
||||||
|
|
||||||
if (!$projectDirectory->exists() || !$projectDirectory->isDirectory()) {
|
if (!$projectDirectory->exists() || !$projectDirectory->isDirectory()) {
|
||||||
$this->style->error('The provided path is not a valid directory.');
|
$this->style->error('The provided path is not a valid directory.');
|
||||||
|
@ -52,9 +54,58 @@ class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface
|
||||||
$this->filePaths->add($projectDirectory, 'project');
|
$this->filePaths->add($projectDirectory, 'project');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($input->hasArgument('name')) {
|
||||||
|
$this->filePaths->add(
|
||||||
|
new SpinnerFilePath(sprintf('data/environments/%s', $input->getArgument('name'))),
|
||||||
|
'projectData'
|
||||||
|
);
|
||||||
|
$this->filePaths->add(
|
||||||
|
new SpinnerFilePath(sprintf('data/environments/%s/.env', $input->getArgument('name'))),
|
||||||
|
'projectEnv'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
protected function getDefaultConfig()
|
||||||
|
{
|
||||||
|
if (!$this->filePaths->get('defaultSpinnerConfig')?->exists()) {
|
||||||
|
throw new \Exception('Default spinner configuration file not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return Yaml::parseFile($this->filePaths->get('defaultSpinnerConfig')->getAbsolutePath())['options'] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildDockerComposeCommand(string $command, bool $daemon = true): string
|
||||||
|
{
|
||||||
|
return sprintf(
|
||||||
|
'cd %s && docker compose %s --env-file=%s%s',
|
||||||
|
$this->filePaths->get('config')->getAbsolutePath(),
|
||||||
|
$command,
|
||||||
|
$this->filePaths->get('projectEnv')->getAbsolutePath()
|
||||||
|
?? $this->filePaths->get('projectEnv')->getProvidedPath(),
|
||||||
|
$daemon ? ' -d' : ''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
|
protected function getDefaultPhpVersion(): ?float
|
||||||
|
{
|
||||||
|
if (!$this->filePaths->get('defaultSpinnerConfig')?->exists()) {
|
||||||
|
throw new \Exception('Default spinner configuration file not found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
$config = $this->getDefaultConfig();
|
||||||
|
|
||||||
|
return $config['environment']['php']['version'] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
@ -64,6 +115,9 @@ class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface
|
||||||
{
|
{
|
||||||
$this->filePaths = new FilePathCollection([
|
$this->filePaths = new FilePathCollection([
|
||||||
'config' => new SpinnerFilePath('config'),
|
'config' => new SpinnerFilePath('config'),
|
||||||
|
'defaultSpinnerConfig' => new SpinnerFilePath('config/spinner.yaml'),
|
||||||
|
'envTemplate' => new SpinnerFilePath('config/.template.env'),
|
||||||
|
'data' => new SpinnerFilePath('data'),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,24 +8,49 @@ use Symfony\Component\Console\Attribute\AsCommand;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Input\InputOption;
|
||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
#[AsCommand(name: 'spin', description: 'Spin up a new development environment')]
|
#[AsCommand(name: 'spin:up', description: 'Spin up a new development environment')]
|
||||||
class SpinCommand extends AbstractSpinnerCommand
|
class SpinCommand extends AbstractSpinnerCommand
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @throws \Exception
|
||||||
|
*/
|
||||||
protected function configure(): void
|
protected function configure(): void
|
||||||
{
|
{
|
||||||
$this
|
$this
|
||||||
->addArgument('name', InputArgument::REQUIRED, 'The name for your Docker container.')
|
->addArgument('name', InputArgument::REQUIRED, 'The name for your Docker container.')
|
||||||
->addArgument('path', InputArgument::REQUIRED, 'The absolute path to your projects root directory.');
|
->addArgument('path', InputArgument::REQUIRED, 'The absolute path to your projects root directory.')
|
||||||
|
->addOption('php', null, InputOption::VALUE_REQUIRED, 'The PHP version to use (e.g., 8.0).', $this->getDefaultPhpVersion());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||||
{
|
{
|
||||||
if (!parent::execute($input, $output)) {
|
if (parent::execute($input, $output)) {
|
||||||
return Command::FAILURE;
|
return Command::FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->filePaths->get('projectData')->exists()) {
|
||||||
|
$this->style->warning('Project data already exists. Skipping new build.');
|
||||||
|
|
||||||
|
return Command::SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->style->success("Spinning up a new development environment...");
|
||||||
|
|
||||||
|
mkdir($this->filePaths->get('projectData')->getProvidedPath(), 0777, true);
|
||||||
|
|
||||||
|
file_put_contents(
|
||||||
|
$this->filePaths->get('projectEnv')->getProvidedPath(),
|
||||||
|
sprintf(
|
||||||
|
file_get_contents($this->filePaths->get('envTemplate')->getAbsolutePath()),
|
||||||
|
$this->filePaths->get('project')->getAbsolutePath(),
|
||||||
|
$input->getArgument('name'),
|
||||||
|
$input->getOption('php')
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return Command::SUCCESS;
|
return Command::SUCCESS;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue