Compare commits

..

No commits in common. "main" and "1.0.0" have entirely different histories.
main ... 1.0.0

13 changed files with 36 additions and 124 deletions

View file

@ -2,43 +2,8 @@
All notable changes to this project will be documented in this file.
## [1.1.2] - 2025-04-26
### Fixed
- Fixed a critical bug where environments would not build correctly if using a SQLite database.
## [1.1.1] - 2025-04-26
### Fixed
- Fixed a critical bug where environments could not be destroyed if using a MySQL database.
- Fixed a critical bug where PDO extensions were not installed in the container, causing MySQL driver errors.
## [1.1.0] - 2025-04-24
### Added
- New database option: MySQL.
- New configuration option: `options.environment.database.rootPassword`.
### Changed
- Changed the default database for new environments to MySQL (from SQLite).
## [1.0.4] - 2025-04-24
### Fixed
- Fixed a critical autoloading issue after the package is globally installed.
## [1.0.3] - 2025-04-24
### Fixed
- Fixed typo in `composer.json` description field.
## [1.0.2] - 2025-04-24
### Added
- Added information on `spin:down` command to README.
### Changed
- Minor documentation tweak to include a link to the Wiki on Packagist.
## [1.0.1] - 2025-04-24
### Changed
- Minor documentation tweak to make the header image display on Packagist.
## [1.0.0] 2025-04-24
### Added
- Initial public release of **Loom Spinner CLI**.
- Command to spin up a new PHP development environment with Docker (`spin:up`).

View file

@ -1,11 +1,11 @@
<p>
<img src="https://forge.winningsoftware.co.uk/LoomLabs/loom-spinner-cli/media/branch/main/header.jpg" alt="Loom Spinner CLI Header Image & Logo" style="width: 100%; height: auto;">
<img src="./header.jpg" alt="Loom Spinner CLI Header Image & Logo" style="width: 100%; height: auto;">
</p>
# Loom Spinner CLI
<p>
<img src="https://img.shields.io/badge/Version-1.1.2-blue" alt="Version 1.1.2">
<img src="https://img.shields.io/badge/Version-1.0.0-blue" alt="Version 1.0.0">
</p>
A streamlined environment management tool for PHP developers.
@ -21,13 +21,16 @@ Effortlessly create custom Docker environments for each of your PHP projects. Ou
- **PHP 8.4** (includes XDebug & OpCache)
- **Nginx**
- **MySQL 9.3**
- **SQLite3**
- **NodeJS 23** (Node, NPM, & NPX)
Your project directory is automatically mounted to the PHP container, and the `public` directory is served via Nginx at
`http://localhost:<nginx-port>`. Access the container directly from your terminal to execute unit tests or other
commands, all within an isolated environment.
> **Note:** Loom Spinner CLI is in early development. For now, only SQLite is supported as the database, but more options
> are on the way in future updates.
# Installation
**Requirements:**
@ -67,13 +70,7 @@ To start them again:
loom spin:start my-project
```
To remove them completely:
```shell
loom spin:down my-project
```
Loom Spinner can be further customized with a set of simple configuration options. For a more detailed quick-start guide
or advanced configuration tips, check out the [project wiki](https://forge.winningsoftware.co.uk/LoomLabs/loom-spinner-cli/wiki).
or advanced configuration tips, check out the project wiki.
Happy spinning! 🧵

View file

@ -7,14 +7,7 @@ use Loom\Spinner\Command\StartCommand;
use Loom\Spinner\Command\StopCommand;
use Symfony\Component\Console\Application;
$localAutoloadPath = sprintf('%s/vendor/autoload.php', dirname(__DIR__));
$installedAutoloadPath = sprintf('%s/autoload.php', dirname(__DIR__, 3));
if (file_exists($localAutoloadPath)) {
require $localAutoloadPath;
} else {
require $installedAutoloadPath;
}
require dirname(__DIR__) . '/vendor/autoload.php';
$application = new Application('Loom Spinner');

View file

@ -1,7 +1,7 @@
{
"name": "loomlabs/loom-spinner-cli",
"description": "A simple command-line Docker environment spinner for PHP.",
"version": "1.1.2",
"decsription": "A simple command-line Docker environment spinner for PHP",
"version": "1.0.0",
"autoload": {
"psr-4": {
"Loom\\Spinner\\": "src/"
@ -13,9 +13,13 @@
}
},
"require": {
"loomlabs/utility.collection": "^1.1",
"symfony/console": "^7.2",
"symfony/yaml": "^7.2"
},
"require-dev": {
"phpunit/phpunit": "^12.1"
},
"bin": [
"bin/loom"
],

View file

@ -7,8 +7,4 @@ PHP_VERSION=%s
PHP_PORT=%s
# Nginx
SERVER_PORT=%s
# Database
DATABASE_PORT=%s
ROOT_PASSWORD=%s
NGINX_PORT=%s

View file

@ -1,16 +0,0 @@
services:
mysql:
image: mysql:9.3.0
command: mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_general_ci
ports:
- ${DATABASE_PORT}:3306
expose:
- "3306"
environment:
MYSQL_ROOT_PASSWORD: ${ROOT_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql:cached
container_name: ${PROJECT_NAME}-mysql
volumes:
mysql_data:

View file

@ -2,9 +2,9 @@ services:
nginx:
build:
context: ./nginx
container_name: ${PROJECT_NAME}-nginx
ports:
- ${SERVER_PORT}:80
- ${NGINX_PORT}:80
volumes:
- ${PROJECT_DIRECTORY}:/var/www/html:cached
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/conf.d:/etc/nginx/conf.d
container_name: ${PROJECT_NAME}-nginx

View file

@ -1 +0,0 @@
RUN docker-php-ext-install mysqli pdo pdo_mysql > /dev/null 2>&1

View file

@ -9,5 +9,4 @@ options:
enabled: true
database:
enabled: true
driver: mysql
rootPassword: docker
driver: sqlite3

View file

@ -5,6 +5,7 @@ declare(strict_types=1);
namespace Loom\Spinner\Classes\File;
use Loom\Spinner\Classes\Config\Config;
use Loom\Utility\FilePath\FilePath;
use Symfony\Component\Console\Input\InputInterface;
class DockerComposeFileBuilder extends AbstractFileBuilder
@ -12,7 +13,7 @@ class DockerComposeFileBuilder extends AbstractFileBuilder
/**
* @throws \Exception
*/
public function __construct(Config $config, private array $ports)
public function __construct(Config $config)
{
return parent::__construct($config->getDataDirectory() . '/docker-compose.yaml', $config);
}
@ -24,20 +25,13 @@ class DockerComposeFileBuilder extends AbstractFileBuilder
{
$this->content = $this->config->getConfigFileContents('php.yaml');
if ($this->config->isDatabaseEnabled($input) && in_array($this->config->getDatabaseDriver($input), ['sqlite3', 'sqlite'])) {
$this->addSqliteDatabaseConfig();
}
if ($this->config->isServerEnabled($input)) {
$this->addNginxConfig();
}
if ($this->config->isDatabaseEnabled($input)) {
$databaseDriver = strtolower($this->config->getDatabaseDriver($input));
if (in_array($databaseDriver, ['sqlite3', 'sqlite'])) {
$this->addSqliteDatabaseConfig();
}
if ($databaseDriver === 'mysql') {
$this->addMysqlDatabaseConfig();
}
}
return $this;
}
@ -62,12 +56,4 @@ class DockerComposeFileBuilder extends AbstractFileBuilder
$sqlLiteConfig = str_replace('volumes:', '', $sqlLiteConfig);
$this->content .= $sqlLiteConfig;
}
private function addMysqlDatabaseConfig(): void
{
$mysqlConfig = str_replace('services:', '', $this->config->getConfigFileContents('mysql.yaml'));
$mysqlConfig = str_replace('${ROOT_PASSWORD}', $this->config->getEnvironmentOption('database', 'rootPassword'), $mysqlConfig);
$mysqlConfig = str_replace('${DATABASE_PORT}', (string) $this->ports['database'], $mysqlConfig);
$this->content.= $mysqlConfig;
}
}

View file

@ -31,23 +31,16 @@ class PHPDockerFileBuilder extends AbstractFileBuilder
$this->config->getConfigFileContents('php-fpm/opcache.ini')
);
if ($this->config->isDatabaseEnabled($input)) {
if (in_array($this->config->getDatabaseDriver($input), ['sqlite3', 'sqlite'])) {
$this->addNewLine();
$this->content .= $this->config->getConfigFileContents('php-fpm/Sqlite.Dockerfile');
}
if ($this->config->getDatabaseDriver($input) ==='mysql') {
$this->addNewLine();
$this->content .= $this->config->getConfigFileContents('php-fpm/MySQL.Dockerfile');
}
if ($this->config->isDatabaseEnabled($input) && in_array($this->config->getDatabaseDriver($input), ['sqlite3', 'sqlite'])) {
$this->addNewLine();
$this->content .= $this->config->getConfigFileContents('php-fpm/Sqlite.Dockerfile');
}
$this->content = str_replace('${NODE_VERSION}', (string) $this->config->getNodeVersion($input), $this->content);
if ($this->config->isXdebugEnabled($input)) {
$this->addNewLine();
$this->content .= $this->config->getConfigFileContents('php-fpm/XDebug.Dockerfile');
$this->content .= $this->config->getConfigFileContents('php-fpm/Xdebug.Dockerfile');
file_put_contents(
$this->config->getDataDirectory() . '/php-fpm/xdebug.ini',
$this->config->getConfigFileContents('php-fpm/xdebug.ini')

View file

@ -29,8 +29,7 @@ class DestroyCommand extends AbstractSpinnerCommand
return Command::FAILURE;
}
$projectName = $input->getArgument('name');
$this->config = new Config($projectName);
$this->config = new Config($input->getArgument('name'));
if (!file_exists($this->config->getDataDirectory())) {
$this->style->error('No project found with the provided name.');
@ -39,7 +38,7 @@ class DestroyCommand extends AbstractSpinnerCommand
}
try {
passthru($this->buildDockerComposeCommand('down -v', false, false));
passthru($this->buildDockerComposeCommand('down', false, false));
recursive_rmdir($this->config->getDataDirectory());
} catch (\Exception $exception) {
$this->style->error('An error occurred while destroying the project: ' . $exception->getMessage());

View file

@ -28,8 +28,7 @@ class SpinCommand extends AbstractSpinnerCommand
$this->portGenerator = new PortGenerator();
$this->ports = [
'php' => $this->portGenerator->generateRandomPort(),
'server' => $this->portGenerator->generateRandomPort(),
'database' => $this->portGenerator->generateRandomPort(),
'nginx' => $this->portGenerator->generateRandomPort(),
];
parent::__construct();
@ -67,7 +66,7 @@ class SpinCommand extends AbstractSpinnerCommand
InputOption::VALUE_NONE,
'Set this flag to not include a database for your environment.'
)
->addOption('database', null, InputOption::VALUE_REQUIRED, 'The type of database to use (e.g., mysql, sqlite).', null, ['mysql', 'sqlite'])
->addOption('database', null, InputOption::VALUE_REQUIRED, 'The type of database to use (e.g., mysql, postgresql, sqlite).', null, ['sqlite'])
->addOption('node', null, InputOption::VALUE_OPTIONAL, 'The Node.js version to use (e.g. 20).');
}
@ -155,9 +154,7 @@ class SpinCommand extends AbstractSpinnerCommand
$input->getArgument('name'),
$this->config->getPhpVersion($input),
$this->ports['php'],
$this->ports['server'],
$this->ports['database'],
$this->config->getEnvironmentOption('database', 'rootPassword')
$this->ports['nginx'],
)
);
}
@ -169,7 +166,7 @@ class SpinCommand extends AbstractSpinnerCommand
{
$this->createProjectDataSubDirectory('php-fpm');
(new DockerComposeFileBuilder($this->config, $this->ports))->build($input)->save();
(new DockerComposeFileBuilder($this->config))->build($input)->save();
}
/**