Spins up basic PHP container

This commit is contained in:
Daniel Winning 2025-04-12 19:09:40 +01:00
parent b8ad06cda3
commit 00b593d6d3
7 changed files with 129 additions and 62 deletions

View file

@ -1,3 +1,6 @@
PROJECT_DIRECTORY=%s PROJECT_DIRECTORY=%s
PROJECT_NAME=%s PROJECT_NAME=%s
PHP_VERSION=%s PHP_VERSION=%s
# Ports
PHP_PORT=%s

View file

@ -1,26 +0,0 @@
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

View file

@ -1,7 +1,3 @@
ARG PHP_VERSION
ENV PHP_VERSION ${PHP_VERSION}
FROM php:${PHP_VERSION}-fpm FROM php:${PHP_VERSION}-fpm
RUN mkdir -p /var/www/html RUN mkdir -p /var/www/html
@ -13,29 +9,29 @@ RUN apt-get -qq update && apt-get -qq install -y \
zip \ zip \
unzip \ unzip \
libicu-dev \ libicu-dev \
imagick \
nano \ nano \
bash \ bash \
dnsutils dnsutils
ENV NVM_DIR /root/.nvm #ENV NVM_DIR /root/.nvm
ENV NODE_VERSION 20 #ENV NODE_VERSION 20
#
RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash \ #RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash \
&& . "$NVM_DIR/nvm.sh" \ # && . "$NVM_DIR/nvm.sh" \
&& nvm install $NODE_VERSION \ # && nvm install $NODE_VERSION \
&& nvm use $NODE_VERSION # && nvm use $NODE_VERSION
#
COPY --from=node:20 /usr/local/bin/npx /usr/local/bin/npx #COPY --from=node:20 /usr/local/bin/npx /usr/local/bin/npx
RUN docker-php-ext-configure intl > /dev/null 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 docker-php-ext-install mysqli pdo pdo_mysql sockets intl exif bcmath > /dev/null
RUN pecl install xdebug redis \ #RUN pecl install xdebug redis \
&& docker-php-ext-enable redis # && docker-php-ext-enable redis
COPY ./xdebug.ini.tmp "${PHP_INI_DIR}/conf.d/xdebug.ini" #COPY ./xdebug.ini.tmp "${PHP_INI_DIR}/conf.d/xdebug.ini"
COPY ./opcache.ini "${PHP_INI_DIR}/conf.d" #RUN docker-php-ext-install opcache > /dev/null
#COPY ./opcache.ini "${PHP_INI_DIR}/conf.d"
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
WORKDIR /var/www/html WORKDIR /var/www/html

View file

@ -1,6 +1,9 @@
php: services:
php:
build: build:
context: ./php-fpm context: ./php-fpm
args:
PHP_VERSION: ${PHP_VERSION}
working_dir: /var/www/html working_dir: /var/www/html
extra_hosts: extra_hosts:
- host.docker.internal:host-gateway - host.docker.internal:host-gateway

View file

@ -0,0 +1,31 @@
<?php
declare(strict_types=1);
namespace Loom\Spinner\Classes\OS;
class PortGenerator
{
public function generateRandomPort(): int
{
$port = 0;
$portInUse = true;
while ($portInUse) {
$port = rand(49152, 65535);
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (!$socket) {
throw new \RuntimeException('Failed to create socket: ' . socket_strerror(socket_last_error()));
}
if (@socket_bind($socket, '127.0.0.1', $port)) {
$portInUse = false;
}
socket_close($socket);
}
return $port;
}
}

View file

@ -63,6 +63,10 @@ class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface
new SpinnerFilePath(sprintf('data/environments/%s/.env', $input->getArgument('name'))), new SpinnerFilePath(sprintf('data/environments/%s/.env', $input->getArgument('name'))),
'projectEnv' 'projectEnv'
); );
$this->filePaths->add(
new SpinnerFilePath(sprintf('data/environments/%s/docker-compose.yml', $input->getArgument('name'))),
'projectDockerCompose'
);
} }
return Command::SUCCESS; return Command::SUCCESS;
@ -83,11 +87,10 @@ class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface
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 %s --env-file=%s%s', 'cd %s && docker-compose --env-file=%s %s%s',
$this->filePaths->get('config')->getAbsolutePath(), $this->filePaths->get('projectData')->getAbsolutePath(),
$this->filePaths->get('projectEnv')->getAbsolutePath(),
$command, $command,
$this->filePaths->get('projectEnv')->getAbsolutePath()
?? $this->filePaths->get('projectEnv')->getProvidedPath(),
$daemon ? ' -d' : '' $daemon ? ' -d' : ''
); );
} }
@ -118,6 +121,9 @@ class AbstractSpinnerCommand extends Command implements ConsoleCommandInterface
'defaultSpinnerConfig' => new SpinnerFilePath('config/spinner.yaml'), 'defaultSpinnerConfig' => new SpinnerFilePath('config/spinner.yaml'),
'envTemplate' => new SpinnerFilePath('config/.template.env'), 'envTemplate' => new SpinnerFilePath('config/.template.env'),
'data' => new SpinnerFilePath('data'), 'data' => new SpinnerFilePath('data'),
'phpYamlTemplate' => new SpinnerFilePath('config/php.yaml'),
'phpFpmDockerfileTemplate' => new SpinnerFilePath('config/php-fpm/Dockerfile'),
'phpFpmDataDirectory' => new SpinnerFilePath('config/php-fpm'),
]); ]);
} }
} }

View file

@ -4,16 +4,31 @@ declare(strict_types=1);
namespace Loom\Spinner\Command; namespace Loom\Spinner\Command;
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;
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\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Yaml\Yaml;
#[AsCommand(name: 'spin:up', 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
{ {
private PortGenerator $portGenerator;
private array $ports;
public function __construct()
{
$this->portGenerator = new PortGenerator();
$this->ports = [
'php' => $this->portGenerator->generateRandomPort(),
];
parent::__construct();
}
/** /**
* @throws \Exception * @throws \Exception
*/ */
@ -39,18 +54,57 @@ class SpinCommand extends AbstractSpinnerCommand
$this->style->success("Spinning up a new development environment..."); $this->style->success("Spinning up a new development environment...");
$this->createProjectData($input);
$command = $this->buildDockerComposeCommand(sprintf('-p %s up', $input->getArgument('name')));
passthru($command);
return Command::SUCCESS;
}
private function createProjectData(InputInterface $input): void
{
mkdir($this->filePaths->get('projectData')->getProvidedPath(), 0777, true); mkdir($this->filePaths->get('projectData')->getProvidedPath(), 0777, true);
$this->createEnvironmentFile($input);
$this->buildDockerComposeFile($input);
}
private function createEnvironmentFile(InputInterface $input): void
{
file_put_contents( file_put_contents(
$this->filePaths->get('projectEnv')->getProvidedPath(), $this->filePaths->get('projectEnv')->getProvidedPath(),
sprintf( sprintf(
file_get_contents($this->filePaths->get('envTemplate')->getAbsolutePath()), file_get_contents($this->filePaths->get('envTemplate')->getAbsolutePath()),
$this->filePaths->get('project')->getAbsolutePath(), $this->filePaths->get('project')->getAbsolutePath(),
$input->getArgument('name'), $input->getArgument('name'),
$input->getOption('php') $input->getOption('php'),
$this->getPort('php'),
) )
); );
}
return Command::SUCCESS; private function buildDockerComposeFile(InputInterface $input): void
{
if (!file_exists($this->filePaths->get('projectData')->getProvidedPath() . '/php-fpm')) {
mkdir($this->filePaths->get('projectData')->getProvidedPath() . '/php-fpm', 0777, true);
}
file_put_contents(
$this->filePaths->get('projectDockerCompose')->getProvidedPath(),
file_get_contents($this->filePaths->get('phpYamlTemplate')->getAbsolutePath())
);
$phpFpmDockerfileTemplate = file_get_contents($this->filePaths->get('phpFpmDockerfileTemplate')->getAbsolutePath());
$phpFpmDockerfileTemplate = str_replace('${PHP_VERSION}', $input->getOption('php'), $phpFpmDockerfileTemplate);
file_put_contents(
$this->filePaths->get('projectData')->getProvidedPath() . '/php-fpm/Dockerfile',
$phpFpmDockerfileTemplate
);
}
private function getPort(string $service): int
{
return $this->ports[$service] ?? $this->portGenerator->generateRandomPort();
} }
} }