Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
"illuminate/support": "^10.3",
"league/flysystem": "^3.0",
"symfony/console": "^6.2",
"symfony/finder": "^6.2"
"symfony/finder": "^6.2",
"symfony/process": "^6.2",
"vlucas/phpdotenv": "^5.5"
},
"require-dev": {
"mockery/mockery": "^1.5.1",
Expand Down
23 changes: 20 additions & 3 deletions devstack
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,30 @@ if (file_exists($autoload = __DIR__ . '/vendor/autoload.php')) {
require dirname(__DIR__, 2) . '/autoload.php';
}

use Symfony\Component\Console\Application;
use Webteractive\Devstack\App;
use Webteractive\Devstack\Commands\MySql;
use Webteractive\Devstack\Commands\Redis;
use Webteractive\Devstack\Commands\Shell;
use Webteractive\Devstack\Commands\Config;
use Webteractive\Devstack\Commands\DownloadRuntimes;
use Webteractive\Devstack\Commands\InitStack;
use Webteractive\Devstack\Commands\RunPHPCommand;
use Webteractive\Devstack\Commands\DownloadRuntimes;
use Webteractive\Devstack\Commands\RunComposerCommands;
use Webteractive\Devstack\RegisterDockerComposeCommands;
use Webteractive\Devstack\Commands\RunLaravelArtisanCommand;

$app = new App('Devstack', '1.1.5');

$app = new Application;
$app->add(new InitStack);
$app->add(new Config);
$app->add(new DownloadRuntimes);
$app->add(new RunLaravelArtisanCommand);
$app->add(new RunPHPCommand);
$app->add(new RunComposerCommands);
$app->add(new Shell);
$app->add(new MySql);
$app->add(new Redis);

RegisterDockerComposeCommands::register($app);

$app->run();
43 changes: 43 additions & 0 deletions src/App.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

namespace Webteractive\Devstack;

use Symfony\Component\Console\Application;

class App extends Application
{
private static $name = "MyApp";
private static $logo = <<<LOGO
DDDDDDDDDDDDD tttt kkkkkkkk
D::::::::::::DDD ttt:::t k::::::k
D:::::::::::::::DD t:::::t k::::::k
DDD:::::DDDDD:::::D t:::::t k::::::k
D:::::D D:::::D eeeeeeeeeeee vvvvvvv vvvvvvv ssssssssss ttttttt:::::ttttttt aaaaaaaaaaaaa cccccccccccccccc k:::::k kkkkkkk
D:::::D D:::::D ee::::::::::::ee v:::::v v:::::v ss::::::::::s t:::::::::::::::::t a::::::::::::a cc:::::::::::::::c k:::::k k:::::k
D:::::D D:::::D e::::::eeeee:::::eev:::::v v:::::vss:::::::::::::s t:::::::::::::::::t aaaaaaaaa:::::a c:::::::::::::::::c k:::::k k:::::k
D:::::D D:::::De::::::e e:::::e v:::::v v:::::v s::::::ssss:::::stttttt:::::::tttttt a::::ac:::::::cccccc:::::c k:::::k k:::::k
D:::::D D:::::De:::::::eeeee::::::e v:::::v v:::::v s:::::s ssssss t:::::t aaaaaaa:::::ac::::::c ccccccc k::::::k:::::k
D:::::D D:::::De:::::::::::::::::e v:::::v v:::::v s::::::s t:::::t aa::::::::::::ac:::::c k:::::::::::k
D:::::D D:::::De::::::eeeeeeeeeee v:::::v:::::v s::::::s t:::::t a::::aaaa::::::ac:::::c k:::::::::::k
D:::::D D:::::D e:::::::e v:::::::::v ssssss s:::::s t:::::t tttttta::::a a:::::ac::::::c ccccccc k::::::k:::::k
DDD:::::DDDDD:::::D e::::::::e v:::::::v s:::::ssss::::::s t::::::tttt:::::ta::::a a:::::ac:::::::cccccc:::::ck::::::k k:::::k
D:::::::::::::::DD e::::::::eeeeeeee v:::::v s::::::::::::::s tt::::::::::::::ta:::::aaaa::::::a c:::::::::::::::::ck::::::k k:::::k
D::::::::::::DDD ee:::::::::::::e v:::v s:::::::::::ss tt:::::::::::tt a::::::::::aa:::a cc:::::::::::::::ck::::::k k:::::k
DDDDDDDDDDDDD eeeeeeeeeeeeee vvv sssssssssss ttttttttttt aaaaaaaaaa aaaa cccccccccccccccckkkkkkkk kkkkkkk
LOGO;

public function __construct($name = 'UNKNOWN', $version = 'UNKNOWN')
{
$this->setName(static::$name);
$this->setVersion($version);
parent::__construct($name, $version);
}

/**
* @return string
*/
public function getHelp(): string
{
return static::$logo . "\n\n" . parent::getHelp();
}
}
2 changes: 1 addition & 1 deletion src/CommandSignature.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@

use Illuminate\Support\Str;
use InvalidArgumentException;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;

class CommandSignature
{
Expand Down
31 changes: 25 additions & 6 deletions src/Commands/Base.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

namespace Webteractive\Devstack\Commands;

use Webteractive\Devstack\CommandSignature;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Webteractive\Devstack\CommandSignature;

abstract class Base extends Command
{
protected $fullCommandSignature;

protected $name;

protected $signature;
Expand All @@ -20,12 +22,12 @@ abstract class Base extends Command

protected $hidden = false;

protected $input;
protected InputInterface $input;

protected $output;
protected OutputInterface $output;

public function __construct()
{
{
if (isset($this->signature)) {
$this->setup();
} else {
Expand All @@ -43,20 +45,32 @@ public function setup()
{
[$name, $arguments, $options] = CommandSignature::parse($this->signature);


if ($this->shouldIgnoreValidationErrors()) {
$this->ignoreValidationErrors();
}

parent::__construct($name);

$this->getDefinition()->addArguments($arguments);
$this->getDefinition()->addOptions($options);
}

public function shouldIgnoreValidationErrors(): bool
{
return false;
}

protected function execute(InputInterface $input, OutputInterface $output)
{
return $this->setIO($input, $output)
->handle();
}

public function setIO($input, $output)
public function setIO(InputInterface $input, OutputInterface $output)
{
global $argv;
$this->fullCommandSignature = array_slice($argv, 2);
$this->input = $input;
$this->output = $output;
return $this;
Expand Down Expand Up @@ -115,6 +129,11 @@ public function line($message = '')
return $this;
}

public function lineBreak()
{
return $this->line('');
}

public function info($message = '')
{
$this->output->writeln(empty($message) ? '' : "<info>{$message}</info>");
Expand Down
6 changes: 0 additions & 6 deletions src/Commands/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,7 @@

namespace Webteractive\Devstack\Commands;

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;
use Webteractive\Devstack\ShouldConfigure;
use Webteractive\Devstack\WithStorage;

class Config extends Base
{
Expand Down
2 changes: 1 addition & 1 deletion src/Commands/DownloadRuntimes.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

namespace Webteractive\Devstack\Commands;

use Webteractive\Devstack\RuntimeDownloader;
use Webteractive\Devstack\ShouldConfigure;
use Webteractive\Devstack\RuntimeDownloader;

class DownloadRuntimes extends Base
{
Expand Down
4 changes: 2 additions & 2 deletions src/Commands/InitStack.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@

namespace Webteractive\Devstack\Commands;

use Webteractive\Devstack\RuntimeDownloader;
use Webteractive\Devstack\File;
use Webteractive\Devstack\PublicRuntimeDownloader;
use Webteractive\Devstack\ShouldConfigure;
use Webteractive\Devstack\RuntimeDownloader;
use Webteractive\Devstack\PublicRuntimeDownloader;

class InitStack extends Base
{
Expand Down
79 changes: 79 additions & 0 deletions src/Commands/MySql.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

namespace Webteractive\Devstack\Commands;

use Webteractive\Devstack\Env;
use Webteractive\Devstack\Process;
use Webteractive\Devstack\WithSignalHandlers;

class MySql extends Base
{
use WithSignalHandlers;

protected $signature = 'mysql
{--p|password= : The password to use.}
{--u|user= : The user to use.}
{--db|database= : The database to use.}
{--env= : The path to the .env file. Default\'s to the current working directory.}';
protected $description = 'Start a MySQL CLI session within the <comment>mysql</comment> container.';

public function handle(): int
{
$defaults = [
'password' => 'password',
'user' => 'user',
'database' => null,
];
$envPath = $this->input->getOption('env') ?? getcwd();
if (file_exists($envPath . '/.env')) {
$env = new Env($envPath);
$defaults['password'] = $env->get('DB_PASSWORD');
$defaults['user'] = $env->get('DB_USERNAME');
$defaults['database'] = $env->get('DB_DATABASE');
$this->lineBreak();
$this->line('Found an <comment>.env</comment> file in your current working directory, values will now be used as defaults.');
$this->lineBreak();
} else {
$this->lineBreak();
$this->line('Unable to find an <comment>.env</comment> file in your current working directory, now using the defaults.');
$this->line('If these were changed in your <comment>docker-compose.yml</comment> file, please supply it as a command');
$this->line('flag or add an <comment>.env</comment> file and add it there.');

$this->lineBreak();
$this->line('If you want to use the .env route, create a .env file and add the variables below including the values:');
$this->line('DB_USERNAME=');
$this->line('DB_PASSWORD=');
$this->line('DB_DATABASE=');
$this->lineBreak();
$this->line('If your .env is located somewhere else, you may use the <comment>--env=/path/to/your/.env</comment> flag.');
$this->line('For example, <comment>devstack mysql --env=/path/to/your/.env/directory</comment>.');
$this->lineBreak();
$this->line('Finally for the command flag, just do <comment>devstack mysql --user=the_user --password=the_password --database=the_db</comment>.');
$this->line('For more details on the <comment>devsack mysql</comment> command flags, run <comment>devstack help mysql</comment>.');
$this->lineBreak();
}


$password = $this->input->getOption('password') ?? $defaults['password'];
$user = $this->input->getOption('user') ?? $defaults['user'];
$database = $this->input->getOption('database') ?? $defaults['database'];

$bashCommand = [];
$bashCommand[] = "MYSQL_PWD={$password}";
$bashCommand[] = "mysql -u {$user}";
if ($database) {
$bashCommand[] = $database;
}

$this->handleTerminationSignals(
$process = Process::prepareFromShell('docker compose exec -it mysql bash -c "' . join(' ', $bashCommand) . '"')
);

$process->setTty(true)
->setTimeout(60 * 60 * 2)
->setIdleTimeout(60 * 60 * 8)
->run();

return static::SUCCESS;
}
}
29 changes: 29 additions & 0 deletions src/Commands/Redis.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Webteractive\Devstack\Commands;

use Webteractive\Devstack\Process;
use Webteractive\Devstack\WithSignalHandlers;

class Redis extends Base
{
use WithSignalHandlers;

protected $signature = 'redis';
protected $description = 'Start a Redis CLI session within the <comment>redis</comment> container.';

public function handle(): int
{

$this->handleTerminationSignals(
$process = Process::prepareFromShell('docker compose exec -it redis redis-cli')
);

$process->setTty(true)
->setTimeout(60 * 60 * 2)
->setIdleTimeout(60 * 60 * 8)
->run();

return static::SUCCESS;
}
}
42 changes: 42 additions & 0 deletions src/Commands/RunComposerCommands.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace Webteractive\Devstack\Commands;

use Webteractive\Devstack\Process;
use Webteractive\Devstack\WithSignalHandlers;

class RunComposerCommands extends Base
{
use WithSignalHandlers;

protected $signature = 'composer';
protected $description = 'Run composer commands.';

public function shouldIgnoreValidationErrors(): bool
{
return true;
}

public function handle(): int
{

$command = [
'docker',
'compose',
'exec',
'-u',
'dev',
'-T',
'app',
'composer',
];

$this->handleTerminationSignals(
$process = Process::prepare(array_merge($command, $this->fullCommandSignature))
);

$process->setTty(true)->run();

return static::SUCCESS;
}
}
Loading