Skip to content
This repository has been archived by the owner on Jun 12, 2022. It is now read-only.

Commit

Permalink
A bunch of work on the releases commands.
Browse files Browse the repository at this point in the history
Moved some release logic into a new `ReleaseService`.
Fleshed out the `activate`, `prune` and `rollback` commands.
  • Loading branch information
warrickbayman committed Mar 12, 2020
1 parent c534b75 commit 31a111e
Show file tree
Hide file tree
Showing 6 changed files with 204 additions and 48 deletions.
14 changes: 14 additions & 0 deletions src/Console/ReleasesActivateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Symfony\Component\Console\Command\Command as SymfonyCommand;
use Symfony\Component\Console\Input\InputArgument;
use TPG\Attache\ReleaseService;

/**
* Class ReleasesActivateCommand.
Expand All @@ -30,6 +31,19 @@ protected function configure()
*/
protected function fire(): int
{
$server = $this->config->server($this->argument('server'));
$id = $this->argument('release');

$releaseService = (new ReleaseService($server))->fetch();

if ($id !== 'latest' && ! $releaseService->exists($id)) {
throw new \RuntimeException('A release with ID '.$id.' does not exist');
}

$this->output->writeln('Setting active release to <info>'.$id.'</info>...');

$releaseService->activate($id);

return 0;
}
}
49 changes: 3 additions & 46 deletions src/Console/ReleasesListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Symfony\Component\Console\Command\Command as SymfonyCommand;
use Symfony\Component\Console\Style\SymfonyStyle;
use TPG\Attache\Exceptions\ConfigurationException;
use TPG\Attache\ReleaseService;
use TPG\Attache\Ssh;

/**
Expand Down Expand Up @@ -35,15 +36,9 @@ protected function fire(): int
{
$server = $this->getServer();

$command = $this->getCommand($server);
$releaseService = (new ReleaseService($server))->fetch();

(new Ssh($server))->run($command, function ($output) use ($server) {
$releases = $this->getReleasesFromOutput($output);

$active = $this->getActiveFromOutput($output);

$this->showOutput($releases, $active);
});
$this->showOutput($releaseService->list(), $releaseService->active());

return 0;
}
Expand All @@ -59,44 +54,6 @@ protected function getServer(): array
return $this->config->server($this->argument('server'));
}

/**
* The command we'll execute on the server.
*
* @param array $server
* @return string
*/
protected function getCommand(array $server): string
{
return 'ls '.$server['root'].'/releases && ls -l '.$server['root'];
}

/**
* Get an array of release IDs from the output returned after execution.
*
* @param array $output
* @return array
*/
protected function getReleasesFromOutput(array $output): array
{
return array_filter(
preg_split('/\n/m', $output[0]['data']),
fn ($release) => $release !== ''
);
}

/**
* Get a string ID of the currently active release.
*
* @param array $output
* @return string
*/
protected function getActiveFromOutput(array $output): string
{
preg_match('/live.*\/(?<id>.+)/', $output[1]['data'], $matches);

return Arr::get($matches, 'id');
}

/**
* Show the output on the console.
*
Expand Down
48 changes: 47 additions & 1 deletion src/Console/ReleasesPruneCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

use Symfony\Component\Console\Command\Command as SymfonyCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use TPG\Attache\ReleaseService;

/**
* Class ReleasesPruneCommand.
Expand All @@ -19,7 +22,8 @@ public function configure()
{
$this->setName('releases:prune')
->setDescription('Prune releases from the specified server. Retains the most recent two')
->addArgument('server', InputArgument::REQUIRED, 'The name of the configured server');
->addArgument('server', InputArgument::REQUIRED, 'The name of the configured server')
->addOption('count','o', InputOption::VALUE_REQUIRED, 'The number of releases to prune.');

$this->requiresConfig();
}
Expand All @@ -29,6 +33,48 @@ public function configure()
*/
public function fire(): int
{
$server = $this->config->server($this->argument('server'));

$releaseService = (new ReleaseService($server))->fetch();

$pruneIds = $this->getIdsToDelete($releaseService->list(), $this->option('count'));

if (!$pruneIds) {
$this->output->writeln('<error>There are no releases to prune</error>');
exit(1);
}

if (!$this->confirmDeletion($pruneIds)) {
$this->output->writeln('Cancelled.');
exit(1);
}

$releaseService->delete($pruneIds);

$this->output->writeln('Pruned '.count($pruneIds).' releases from <info>'.$server['name'].'</info>');

return 0;
}

protected function getIdsToDelete(array $releases, ?int $count = null): array
{
if (!$count || $count > count($releases) - 2) {
$count = count($releases) - 2;
}

return array_slice($releases, 0, $count);
}

protected function confirmDeletion(array $ids): bool
{
$helper = $this->getHelper('question');
$question = new ConfirmationQuestion(
'you are about to delete <info>'.count($ids).
'</info> releases.'."\n".
'<error>This action cannot be undone</error>. Are you sure? (y/N): ',
false
);

return $helper->ask($this->input, $this->output, $question);
}
}
25 changes: 25 additions & 0 deletions src/Console/ReleasesRollbackCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

use Symfony\Component\Console\Command\Command as SymfonyCommand;
use Symfony\Component\Console\Input\InputArgument;
use TPG\Attache\ReleaseService;
use TPG\Attache\Ssh;

/**
* Class ReleasesRollbackCommand.
Expand All @@ -29,6 +31,29 @@ protected function configure(): void
*/
protected function fire(): int
{
$server = $this->config->server($this->argument('server'));

$releaseService = (new ReleaseService($server))->fetch();

$activeIndex = array_search($releaseService->active(), $releaseService->list(), true);

if ($activeIndex === false) {
throw new \RuntimeException('Could not determine the currently active release');
}

if ($activeIndex > 0) {
$rollbackId = $releaseService->list()[$activeIndex -1];

$command = 'ln -nfs '.$server['root'].'/releases/'.$rollbackId.' '.
$server['root'].'/live';

(new Ssh($server))->run($command, function ($outputs) use ($rollbackId) {

$this->output->writeln('Rolled back to <info>'.$rollbackId.'</info>');

});
}

return 0;
}
}
114 changes: 114 additions & 0 deletions src/ReleaseService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
<?php

namespace TPG\Attache;

use Illuminate\Support\Arr;

class ReleaseService
{
/**
* @var array
*/
protected array $server;

protected array $releases;

protected string $active;

/**
* Release constructor.
* @param array $server
*/
public function __construct(array $server)
{
$this->server = $server;
}

public function fetch(): self
{
$command = 'ls '.$this->server['root'].'/releases && ls -l '.$this->server['root'];

(new Ssh($this->server))->run($command, function ($output) {

$this->releases = $this->getReleasesFromOutput($output);
$this->active = $this->getActiveFromOutput($output);

});

return $this;
}

/**
* Get an array of release IDs from the output returned after execution.
*
* @param array $output
* @return array
*/
protected function getReleasesFromOutput(array $output): array
{
return array_filter(
preg_split('/\n/m', $output[0]['data']),
fn ($release) => $release !== ''
);
}

/**
* Get a string ID of the currently active release.
*
* @param array $output
* @return string
*/
protected function getActiveFromOutput(array $output): string
{
preg_match('/live.*\/(?<id>.+)/', $output[1]['data'], $matches);

return Arr::get($matches, 'id');
}

public function list(): array
{
return $this->releases;
}

public function active(): string
{
return $this->active;
}

public function exists(string $id): bool
{
return in_array($id, $this->releases, true);
}

public function activate(string $id): void
{
if ($id === 'latest') {
$id = $this->releases[count($this->releases) -1];
}

$command = 'ln -nfs '.$this->server['root'].'/releases/'.$id.' '.
$this->server['root'].'/live';

(new Ssh($this->server))->run($command, function ($outputs) {

//

});
}

public function delete(array $ids): void
{
$commands = [];
foreach ($ids as $id) {
$commands[] = 'rm -rf '.$this->server['root'].'/releases/'.$id;
}

$command = implode(' && ', $commands);

(new Ssh($this->server))->run($command, function ($outputs) use ($ids) {

//

});
}
}
2 changes: 1 addition & 1 deletion src/Ssh.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function run($commands, \Closure $callback = null)
self::DELIMITER
);

$process->run(function ($type, $output) use (&$outputs) {
$process->run(function ($type, $output = null) use (&$outputs) {
if ($type === Process::OUT) {
$outputs[] = [
'type' => $type,
Expand Down

0 comments on commit 31a111e

Please sign in to comment.