Repository URL to install this package:
|
Version:
2.2.0 ▾
|
<?php
namespace MeltConsole\App\Commands;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ChoiceQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Question\ConfirmationQuestion;
/**
* Handles the functionality to create new environments on the server.
*
* @package MeltConsole\App\Commands
*/
class CreateEnvironmentCommand extends MeltCommand {
/**
* Question helper.
*
* @var \Symfony\Component\Console\Helper\QuestionHelper
*/
private $question;
/**
* {@inheritdoc}
*/
protected function configure() {
// Initial config for command.
$this
->setName('melt:create-environment')
->setAliases(['melt:ce'])
->setDescription('Creates new environment on the remote server.')
->setHelp('This command allows you to create a user...')
->addOption('env', 'e', InputOption::VALUE_REQUIRED, 'Environment e.g dev, qa, stage')
->addOption('branch', 'b', InputOption::VALUE_REQUIRED, 'Branch to use');
}
/**
* {@inheritdoc}
*/
protected function interact(InputInterface $input, OutputInterface $output) {
parent::interact($input, $output);
$env = NULL;
$this->question = $this->getHelper('question');
if (!$input->getOption('env')) {
// Ask the user some stuff.
$helper = $this->getHelper('question');
$question = new Question('Enter the environment name you\'d like to create e.g. dev, stage, qa, prod: ');
$answer = $helper->ask($input, $output, $question);
$env = strtolower(preg_replace('/\s+/', '_', trim($answer)));
if (empty($env)) {
throw new \Exception('Please enter a value for environment name.');
}
// Set options based on users answer.
$input->setOption('env', $env);
}
if ($env && !$input->getOption('branch')) {
$remotes = $this->getRemoteBranches();
$question = new ChoiceQuestion("Which branch would you like deployed to <info>{$env}</info> environment? ", $remotes);
$branch = $this->question->ask($input, $output, $question);
$input->setOption('branch', $branch);
}
}
/**
* {@inheritdoc}
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$env = $input->getOption('env');
$branch = $input->getOption('branch');
if (!$this->validateEnvironmentExistsOnServer($env)) {
$dirname = "{$this->projectName}-{$env}";
$helper = $this->getHelper('question');
// Ask questions.
$question1 = new ConfirmationQuestion(
"Do you really want to create a <info>{$dirname}</info> environment? (n/Y) ",
TRUE
);
$proceed = $helper->ask($input, $output, $question1);
$serverEnvs = $this->getEnvironmentsFromServer();
$chosenEnv = NULL;
if (!empty($serverEnvs)) {
$serverEnvs[] = 'none';
$question2 = new ChoiceQuestion(
'What environment would you like to sync with?',
$serverEnvs
);
$chosenEnv = $helper->ask($input, $output, $question2);
}
if ($proceed) {
// Clone project directory.
$output->writeln("Creating <info>{$dirname}</info> environment...");
$this->cloneNewEnvironment($env, $branch);
$this->setupServer($env);
// Install dependencies.
$this->runServerCommands([
"cd /{$this->serverRoot}/{$dirname}",
'composer install --no-dev --optimize-autoloader',
]);
// Sync database and files if they chose an existing environment
if ($chosenEnv && $chosenEnv !== 'none') {
// Sync database with another environment.
$output->writeln("Syncing database with <info>{$this->projectName}-{$chosenEnv}</info>...");
$this->syncDatabaseWithAnotherEnvironment($chosenEnv, $env);
// Sync files with another environment.
$output->writeln('Syncing files now...');
$this->syncFilesWithAnotherEnvironment($chosenEnv, $env);
$output->writeln("Syncing complete!");
}
// @TODO Create brand new environment if they
// commenting this out for now because we may not need to do this
// if($chosenEnv == 'none') {
// // @TODO automatically install drupal for user. Figure out a better way.
// $this->runServerCommands([
// "cd /{$this->serverRoot}/{$dirname}",
// 'drush si',
// ]);
// }
$output->writeln("Creation of <info>{$dirname}</info> complete!");
// Add drush alias.
$output->writeln("Adding drush alias for <info>${dirname}</info>...");
$this->addDrushAliasForEnv($env);
$output->writeln("Baddabing baddaboom! Drush alias created!!! Go check <info>{$this->pathToDrushAlias}</info>. You're welcome!");
// Useful output.
$output->writeln([
'----------------------',
"Environment URL: <info>https://{$this->projectName}-{$env}.drupal.meltdemo.com</info>",
"Drush alias example: <info>lando drush @melt.{$env} ssh lando drush pm-list</info>",
'----------------------',
]);
}
}
else {
throw new \Exception(sprintf(
'%s environment already exists on the server. Please choose another environment name.',
$env
));
}
}
/**
* Create's new directory on server by cloning the git repo.
*
* @param string $env
* Directory name.
* @param string $branch
* Git branch name.
*/
private function cloneNewEnvironment(string $env, string $branch) {
$config = $this->getConfig();
$dirname = "{$this->projectName}-{$env}";
$giturl = $config['melt']['git'];
$this->runServerCommands([
"cd /{$this->serverRoot}",
"git clone -b {$branch} {$giturl} {$dirname}",
"cd {$dirname}"
]);
}
/**
* Helper for `melt:ss` command.
*
* @param string $env
* Environment name
*/
private function setupServer(string $env) {
$dirname = "{$this->projectName}-{$env}";
$this->runServerCommands([
"cd /{$this->serverRoot}/{$dirname}",
"meltconsole melt:ss --projectName={$dirname} --projectRoot=/{$this->serverRoot}/{$dirname}",
]);
}
/**
* Syncs files between environments.
*
* --TODO-- may need to refactor this to use some other mechanism of importing
* files from one environment to another. This works for now since they're
* on the same server.
*
* @param string $srcEnv
* Source environment.
* @param string $destEnv
* Destination environment.
*/
private function syncFilesWithAnotherEnvironment(string $srcEnv, string $destEnv) {
// Create destination dirname.
$dest = "{$this->projectName}-{$destEnv}";
$filesDir = 'docroot/sites/default/files';
$src = "{$this->projectName}-{$srcEnv}";
$this->runServerCommands([
// Make sure `files` dir exists before rsyncing
"[ -d /{$this->serverRoot}/{$src}/{$filesDir} ]", // Make sure `files` dir exists before rsyncing
// Rsync files from source directory to destination
"rsync -az /{$this->serverRoot}/{$src}/{$filesDir}/ /{$this->serverRoot}/{$dest}/{$filesDir}/",
]);
}
/**
* Syncs database between environments.
*
* --TODO-- may need to refactor this to use some other mechanism of importing
* a database from one environment to another. This works for now since
* they're on the same server.
*
* @param string $srcEnv
* Source environment.
* @param string $destEnv
* Destination environment.
*/
private function syncDatabaseWithAnotherEnvironment(string $srcEnv, string $destEnv) {
// Create destination dirname.
$dest = "{$this->projectName}-{$destEnv}";
$src = "{$this->projectName}-{$srcEnv}";
// Year month day hour minute.
$timestamp = date('Ymdhi');
$backup_name = "{$srcEnv}-backup-{$timestamp}.sql";
$backup_dir = '/opt/backups';
$this->runServerCommands([
// Change into src directory.
"cd /{$this->serverRoot}/{$src}",
// Add to backups directory
"drush sql:dump --gzip --result-file={$backup_dir}/{$backup_name}",
// Copy backup to destination.
"cp {$backup_dir}/{$backup_name}.gz /{$this->serverRoot}/{$dest}/",
// Change to destination directory.
"cd /{$this->serverRoot}/{$dest}",
// Import the src's backup.
"gunzip -c {$backup_name}.gz | drush sqlc",
// Remove the src's backup from the destination.
"rm {$backup_name}.gz",
]);
}
/**
* Adds a new alias to the @var $pathToDrushAlias.
*
* @param string $env
* Environment name.
*/
protected function addDrushAliasForEnv(string $env) {
$aliases = $this->getDrushAliases();
// Copy an alias for our new alias.
$new_alias[$env] = reset($aliases);
// @TODO figure out a better way to set host and root.
$new_alias[$env]['uri'] = "{$this->projectName}-{$env}.drupal.meltdemo.com";
$new_alias[$env]['root'] = "/{$this->serverRoot}/{$this->projectName}-{$env}";
// Combine new alias with the current.
$updated_aliases = array_merge($aliases, $new_alias);
$this->writeToYamlFile($updated_aliases, $this->pathToDrushAlias);
}
}