Added "init" command to admin-cli
Got to basic working state, some todos in there.
This commit is contained in:
parent
ff0c183a66
commit
3cc761d2d8
3 changed files with 208 additions and 1 deletions
|
@ -66,7 +66,7 @@ final class BackupCommand
|
|||
$zip->close();
|
||||
rename($zipTempFile, $zipOutFile);
|
||||
|
||||
// Announce end and display errors
|
||||
// Announce end
|
||||
echo "Backup finished.\nOutput ZIP saved to: {$zipOutFile}\n";
|
||||
}
|
||||
|
||||
|
|
205
scripts/Commands/InitCommand.php
Normal file
205
scripts/Commands/InitCommand.php
Normal file
|
@ -0,0 +1,205 @@
|
|||
<?php
|
||||
|
||||
namespace Cli\Commands;
|
||||
|
||||
use Minicli\Command\CommandCall;
|
||||
use Symfony\Component\Process\ExecutableFinder;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class InitCommand
|
||||
{
|
||||
/**
|
||||
* @throws CommandError
|
||||
*/
|
||||
public function handle(CommandCall $input)
|
||||
{
|
||||
$this->ensureRequiredExtensionInstalled(); // TODO - Ensure bookstack install deps are met?
|
||||
|
||||
// TODO - Dedupe the command stuff going on.
|
||||
// TODO - Check composer and git exists before running
|
||||
// TODO - Look at better way of handling env usage, on demand maybe where needed?
|
||||
// Env loading in main `run` script if confilicting with certain bits here (app key generate, hence APP_KEY overload)
|
||||
// See dotenv's Dotenv::createArrayBacked as way to go this.
|
||||
// (More of a change for 'backup' command).
|
||||
// TODO - Potentially download composer?
|
||||
|
||||
$suggestedOutPath = $input->subcommand;
|
||||
if ($suggestedOutPath === 'default') {
|
||||
$suggestedOutPath = '';
|
||||
}
|
||||
|
||||
echo "Locating and checking install directory...\n";
|
||||
$installDir = $this->getInstallDir($suggestedOutPath);
|
||||
$this->ensureInstallDirEmpty($installDir);
|
||||
|
||||
echo "Cloning down BookStack project to install directory...\n";
|
||||
$this->cloneBookStackViaGit($installDir);
|
||||
|
||||
echo "Installing application dependencies using composer...\n";
|
||||
$this->installComposerDependencies($installDir);
|
||||
|
||||
echo "Creating .env file from .env.example...\n";
|
||||
copy($installDir . DIRECTORY_SEPARATOR . '.env.example', $installDir . DIRECTORY_SEPARATOR . '.env');
|
||||
sleep(1);
|
||||
|
||||
echo "Generating app key...\n";
|
||||
$this->generateAppKey($installDir);
|
||||
|
||||
// Announce end
|
||||
echo "A BookStack install has been initialized at: {$installDir}\n\n";
|
||||
echo "You will still need to:\n";
|
||||
echo "- Update the .env file in the install with correct URL, database and email details.\n";
|
||||
echo "- Run 'php artisan migrate' to set-up the database.\n";
|
||||
echo "- Configure your webserver for use with BookStack.\n";
|
||||
echo "- Ensure the required directories (storage/ bootstrap/cache public/uploads) are web-server writable.\n";
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the required PHP extensions are installed for this command.
|
||||
* @throws CommandError
|
||||
*/
|
||||
protected function ensureRequiredExtensionInstalled(): void
|
||||
{
|
||||
// if (!extension_loaded('zip')) {
|
||||
// throw new CommandError('The "zip" PHP extension is required to run this command');
|
||||
// }
|
||||
}
|
||||
|
||||
protected function generateAppKey(string $installDir): void
|
||||
{
|
||||
// Find reference to php
|
||||
$executableFinder = new ExecutableFinder();
|
||||
$phpPath = $executableFinder->find('php', '/usr/bin/php');
|
||||
if (!is_file($phpPath)) {
|
||||
throw new CommandError('Could not locate "php" program.');
|
||||
}
|
||||
|
||||
$process = new Process([
|
||||
$phpPath,
|
||||
$installDir . DIRECTORY_SEPARATOR . 'artisan',
|
||||
'key:generate', '--force', '-n', '-q'
|
||||
], null, ['APP_KEY' => 'SomeRandomString']);
|
||||
$process->setTimeout(240);
|
||||
$process->setIdleTimeout(5);
|
||||
$process->start();
|
||||
|
||||
$errors = '';
|
||||
foreach ($process as $type => $data) {
|
||||
// Errors are on stdout for artisan
|
||||
$errors .= $data . "\n";
|
||||
}
|
||||
|
||||
if ($errors) {
|
||||
throw new CommandError("Failed 'php artisan key:generate' with errors:\n" . $errors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run composer install to download PHP dependencies.
|
||||
* @throws CommandError
|
||||
*/
|
||||
protected function installComposerDependencies(string $installDir): void
|
||||
{
|
||||
// Find reference to composer
|
||||
$executableFinder = new ExecutableFinder();
|
||||
$composerPath = $executableFinder->find('composer', '/usr/local/bin/composer');
|
||||
if (!is_file($composerPath)) {
|
||||
throw new CommandError('Could not locate "composer" program.');
|
||||
}
|
||||
|
||||
$process = new Process([
|
||||
$composerPath, 'install',
|
||||
'--no-dev', '-n', '-q', '--no-progress',
|
||||
'-d', $installDir
|
||||
]);
|
||||
$process->setTimeout(240);
|
||||
$process->setIdleTimeout(15);
|
||||
$process->start();
|
||||
|
||||
$errors = '';
|
||||
foreach ($process as $type => $data) {
|
||||
if ($process::ERR === $type) {
|
||||
$errors .= $data . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($errors) {
|
||||
throw new CommandError("Failed composer install with errors:\n" . $errors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone a new instance of BookStack to the given install folder.
|
||||
* @throws CommandError
|
||||
*/
|
||||
protected function cloneBookStackViaGit(string $installDir): void
|
||||
{
|
||||
// Find reference to git
|
||||
$executableFinder = new ExecutableFinder();
|
||||
$gitPath = $executableFinder->find('git', '/usr/bin/bit');
|
||||
if (!is_file($gitPath)) {
|
||||
throw new CommandError('Could not locate "git" program.');
|
||||
}
|
||||
|
||||
$process = new Process([
|
||||
$gitPath, 'clone', '-q',
|
||||
'--branch', 'release',
|
||||
'--single-branch',
|
||||
'https://github.com/BookStackApp/BookStack.git',
|
||||
$installDir
|
||||
]);
|
||||
$process->setTimeout(240);
|
||||
$process->setIdleTimeout(15);
|
||||
$process->start();
|
||||
|
||||
$errors = '';
|
||||
foreach ($process as $type => $data) {
|
||||
if ($process::ERR === $type) {
|
||||
$errors .= $data . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
if ($errors) {
|
||||
throw new CommandError("Failed git clone with errors:\n" . $errors);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that the installation directory is completely empty to avoid potential conflicts or issues.
|
||||
* @throws CommandError
|
||||
*/
|
||||
protected function ensureInstallDirEmpty(string $installDir): void
|
||||
{
|
||||
$contents = array_diff(scandir($installDir), ['..', '.']);
|
||||
if (count($contents) > 0) {
|
||||
throw new CommandError("Expected install directory to be empty but existing files found in [{$installDir}] target location.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a full path to the intended location for the BookStack install.
|
||||
* @throws CommandError
|
||||
*/
|
||||
protected function getInstallDir(string $suggestedDir): string
|
||||
{
|
||||
$dir = getcwd();
|
||||
|
||||
if ($suggestedDir) {
|
||||
if (is_file($suggestedDir)) {
|
||||
throw new CommandError("Was provided [{$suggestedDir}] as an install path but existing file provided.");
|
||||
} else if (is_dir($suggestedDir)) {
|
||||
$dir = realpath($suggestedDir);
|
||||
} else if (is_dir(dirname($suggestedDir))) {
|
||||
$created = mkdir($suggestedDir);
|
||||
if (!$created) {
|
||||
throw new CommandError("Could not create directory [{$suggestedDir}] for install.");
|
||||
}
|
||||
$dir = $suggestedDir;
|
||||
} else {
|
||||
throw new CommandError("Could not resolve provided [{$suggestedDir}] path to an existing folder.");
|
||||
}
|
||||
}
|
||||
|
||||
return $dir;
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ require __DIR__ . '/vendor/autoload.php';
|
|||
|
||||
use Cli\Commands\BackupCommand;
|
||||
use Cli\Commands\CommandError;
|
||||
use Cli\Commands\InitCommand;
|
||||
use Minicli\App;
|
||||
|
||||
// Get the directory of the CLI "entrypoint", adjusted to be the real
|
||||
|
@ -28,6 +29,7 @@ $app = new App();
|
|||
$app->setSignature('./run');
|
||||
|
||||
$app->registerCommand('backup', [new BackupCommand($bsDir), 'handle']);
|
||||
$app->registerCommand('init', [new InitCommand(), 'handle']);
|
||||
|
||||
try {
|
||||
$app->runCommand($argv);
|
||||
|
|
Loading…
Reference in a new issue