Skip to content

Commit

Permalink
Merge pull request #1 from J-T-McC/feature/migration
Browse files Browse the repository at this point in the history
Migrate feature & Laravel 7 Support
  • Loading branch information
J-T-McC authored Jan 21, 2021
2 parents 3483629 + 75591f7 commit 59dd574
Show file tree
Hide file tree
Showing 13 changed files with 215 additions and 48 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ jobs:
matrix:
os: [ubuntu-latest]
php: [7.4, 8.0]
laravel: [8.*]
laravel: [8.*, 7.*]
dependency-version: [prefer-lowest, prefer-stable]
include:
- laravel: 8.*
testbench: 6.*
- laravel: 7.*
testbench: 5.*

name: P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }} - ${{ matrix.os }}

Expand Down
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ The purpose of this package is to introduce local zero-downtime deployments into

## Requirements

* Laravel 8
* Laravel 7 | 8
* PHP ^7.4 | ^8.0

## Installation
Expand Down Expand Up @@ -64,13 +64,26 @@ return [
* Max number of build directories allowed
* Once limit is hit, old deployments will be removed automatically after a successful build
*/
'build-limit' => 10
'build-limit' => 10,

/**
* Migrate files|folders from the outgoing production build to your new release using a relative path and pattern
* @see https://www.php.net/manual/en/function.glob.php
*/
'migrate' => [
// 'storage/framework/sessions/*',
]

];
```

By default, this package will restrict your project to 10 deployment builds. Once you hit the limit defined in the config,
older deployments will be automatically deleted. Be aware of the size of your project and adjust to meet your needs.

You might find yourself in a situation where you need to migrate files that don't exist in your build project from your
current deployment folder to your new deployment folder. These files/folders can be specified in the migrate config array,
and they will be copied from the outgoing deployment into the new deployment when you run the deploy command.

Once you have configured your env and have deployed a build, you can update your webserver to start routing traffic
to your 'deployment-link' location.

Expand Down Expand Up @@ -104,7 +117,7 @@ Deploy current build using the current branch git hash for deployment folder
php artisan atomic-deployments:deploy
```

Deploy current under using a custom directory name
Deploy current build using a custom directory name
```shell script
php artisan atomic-deployments:deploy --directory=deployment_folder
```
Expand Down
11 changes: 10 additions & 1 deletion config/atomic-deployments.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,14 @@
* Max number of build directories allowed
* Once limit is hit, old deployments will be removed automatically after a successful build
*/
'build-limit' => 10
'build-limit' => 10,

/**
* Migrate files|folders from the outgoing production build to your new release using a relative path and pattern
* @see https://www.php.net/manual/en/function.glob.php
*/
'migrate' => [
// 'storage/framework/sessions/*',
]

];
2 changes: 2 additions & 0 deletions src/Commands/DeployCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ public function handle()
$buildPath = config('atomic-deployments.build-path');
$deploymentLink = config('atomic-deployments.deployment-link');
$deploymentsPath = config('atomic-deployments.deployments-path');
$migrate = config('atomic-deployments.migrate', []);

$atomicDeployment = (new AtomicDeployments(
$deploymentLink,
$deploymentsPath,
$buildPath,
$migrate,
$this->option('dry-run')
));

Expand Down
3 changes: 2 additions & 1 deletion src/Commands/ListCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public function handle()
'deployment_link',
'deployment_status',
'created_at',
)->get()->append('isCurrentlyDeployed')->map(function($deployment) {
)->get()->map(function($deployment) {
$deployment->append('isCurrentlyDeployed');
$deployment->deployment_status = DeploymentStatus::getNameFromValue($deployment->deployment_status);
return $deployment;
});
Expand Down
3 changes: 1 addition & 2 deletions src/Models/AtomicDeployment.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace JTMcC\AtomicDeployments\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\File;
use JTMcC\AtomicDeployments\Exceptions\AreYouInsaneException;
Expand All @@ -13,7 +12,7 @@

class AtomicDeployment extends Model
{
use HasFactory, SoftDeletes;
use SoftDeletes;


protected $fillable = [
Expand Down
101 changes: 85 additions & 16 deletions src/Services/AtomicDeployments.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
use JTMcC\AtomicDeployments\Models\Enums\DeploymentStatus;

use Illuminate\Support\Pluralizer;
use Illuminate\Support\Facades\File;

class AtomicDeployments
{

protected ?AtomicDeployment $model = null;

protected bool $dryRun;
protected array $migrate;

protected string $buildPath;
protected string $deploymentLink;
Expand All @@ -34,15 +36,22 @@ class AtomicDeployments
* @param string $deploymentLink
* @param string $deploymentsPath
* @param string $buildPath
* @param array $migrate
* @param bool $dryRun
*
* @throws ExecuteFailedException
*/
public function __construct(string $deploymentLink, string $deploymentsPath, string $buildPath, bool $dryRun = false)
public function __construct(
string $deploymentLink,
string $deploymentsPath,
string $buildPath,
array $migrate = [],
bool $dryRun = false)
{
$this->deploymentLink = $deploymentLink;
$this->deploymentsPath = $deploymentsPath;
$this->buildPath = $buildPath;
$this->migrate = $migrate;
$this->dryRun = $dryRun;

register_shutdown_function([$this, 'shutdown']);
Expand All @@ -57,7 +66,7 @@ public function __construct(string $deploymentLink, string $deploymentsPath, str
* @param Closure|null $success
* @param Closure|null $failed
*/
public function deploy(?Closure $success = null, ?Closure $failed = null)
public function deploy(?Closure $success = null, ?Closure $failed = null): void
{
try {

Expand All @@ -80,6 +89,7 @@ public function deploy(?Closure $success = null, ?Closure $failed = null)
$this->createDeploymentDirectory();
$this->confirmRequiredDirectoriesExist();
$this->copyDeploymentContents();
$this->copyMigrationContents();
$this->linkDeployment($this->deploymentLink, $this->deploymentPath);
$this->confirmSymbolicLink($this->deploymentPath);
$this->updateDeploymentStatus(DeploymentStatus::SUCCESS);
Expand All @@ -104,7 +114,7 @@ public function deploy(?Closure $success = null, ?Closure $failed = null)
*
* @param int $status
*/
public function updateDeploymentStatus(int $status)
public function updateDeploymentStatus(int $status): void
{
if ($this->isDryRun()) {
Output::warn('Dry run - Skipping deployment status update');
Expand Down Expand Up @@ -132,7 +142,7 @@ public function updateDeploymentStatus(int $status)
*
* @throws ExecuteFailedException
*/
public function confirmSymbolicLink(string $deploymentPath)
public function confirmSymbolicLink(string $deploymentPath): bool
{
Output::info('Confirming deployment link is correct');
$currentDeploymentPath = $this->getCurrentDeploymentPath();
Expand All @@ -154,7 +164,7 @@ public function confirmSymbolicLink(string $deploymentPath)
/**
* @throws InvalidPathException
*/
public function confirmRequiredDirectoriesExist()
public function confirmRequiredDirectoriesExist(): void
{
if ($this->isDryRun()) {
Output::warn('Dry run - Skipping required directory exists check for:');
Expand All @@ -176,7 +186,7 @@ public function createDeploymentDirectory(): void
{
Output::info("Creating directory at {$this->deploymentPath}");

if(strpos($this->deploymentPath, $this->buildPath) !== false) {
if (strpos($this->deploymentPath, $this->buildPath) !== false) {
throw new InvalidPathException('Deployments folder cannot be subdirectory of build folder');
}

Expand Down Expand Up @@ -204,11 +214,63 @@ public function copyDeploymentContents(): void
return;
}

Exec::rsyncDir("{$this->buildPath}/", "{$this->deploymentPath}/");
Exec::rsync("{$this->buildPath}/", "{$this->deploymentPath}/");
Output::info('Copying complete');
}


/**
* @throws ExecuteFailedException
*/
public function copyMigrationContents(): void
{
if (!empty($this->initialDeploymentPath) && count($this->migrate)) {

if ($this->isDryRun()) {
Output::warn('Dry run - skipping migrations');
}

collect($this->migrate)->each(function ($pattern) {

if (!$this->isDryRun()) {
Output::info("Running migration for pattern {$pattern}");
}

$rootFrom = rtrim($this->initialDeploymentPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
$rootTo = rtrim($this->deploymentPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;

foreach (File::glob($rootFrom . $pattern) as $from) {

$dir = $from;

if (!File::isDirectory($dir)) {
$dir = File::dirname($dir);
}

$dir = str_replace($rootFrom, $rootTo, $dir);
$to = str_replace($rootFrom, $rootTo, $from);

if ($this->isDryRun()) {
Output::warn("Dry run - migrate: \r\n - {$from}\r\n - {$to}");
Output::line();
continue;
}

File::ensureDirectoryExists($dir, 0755, true);

Exec::rsync($from, $to);

}

if (!$this->isDryRun()) {
Output::info("Finished migration for pattern {$pattern}");
}

});
}
}


/**
* Create Symbolic link for live deployment
* Will overwrite previous link
Expand Down Expand Up @@ -249,10 +311,10 @@ public function setDeploymentDirectory(string $name): void
*
* @throws ExecuteFailedException
*/
public function getCurrentDeploymentPath()
public function getCurrentDeploymentPath(): string
{
$result = Exec::readlink($this->deploymentLink);
if($result === $this->deploymentLink) {
if ($result === $this->deploymentLink) {
return '';
}
return $result;
Expand All @@ -276,7 +338,7 @@ public function setDeploymentPath(): void
*
* @see getCurrentDeploymentPath() to get the path currently in use
*/
public function getDeploymentPath()
public function getDeploymentPath(): string
{
return $this->deploymentsPath;
}
Expand Down Expand Up @@ -333,7 +395,7 @@ public function rollback(): void
* @throws ExecuteFailedException
* @throws InvalidPathException
*/
public function cleanBuilds($limit)
public function cleanBuilds($limit): void
{
Output::alert('Running Build Cleanup');
Output::info("Max deployment directories allowed set to {$limit}");
Expand Down Expand Up @@ -368,23 +430,30 @@ public function cleanBuilds($limit)
}


public function isDryRun() {
/**
* @return bool
*/
public function isDryRun(): bool
{
return $this->dryRun;
}


/**
* @throws ExecuteFailedException
*/
public function failed()
public function failed(): void
{
$this->rollback();
$this->updateDeploymentStatus(DeploymentStatus::FAILED);
DeploymentFailed::dispatch($this, $this->model);
}


public function shutdown()

/**
* @throws ExecuteFailedException
*/
public function shutdown(): void
{
if ($error = error_get_last()) {
Output::error("Error detected during shutdown, requesting rollback");
Expand Down
18 changes: 17 additions & 1 deletion src/Services/Exec.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,27 @@ public static function ln(string $link, string $path)
*
* @throws ExecuteFailedException
*/
public static function rsyncDir(string $from, string $to) {
public static function rsync(string $from, string $to) {
return self::run('rsync -aW --no-compress %s %s', [$from, $to]);
}


/**
* @param string $from
* @param string $to
* @param string $pattern
*
* @return string
*
* @throws ExecuteFailedException
*/
public static function rsyncPattern(string $from, string $to, string $pattern) {
$from = rtrim($from, DIRECTORY_SEPARATOR ) . '/';
$to = rtrim($to, DIRECTORY_SEPARATOR) . '/';
return self::run('rsync -aW --no-compress --include="'.$pattern.'" --exclude="*" %s %s', [$from, $to]);
}


/**
* @return string
*
Expand Down
7 changes: 6 additions & 1 deletion src/Services/Output.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
class Output
{


/**
* Print throwable to console | log
*
Expand Down Expand Up @@ -83,4 +82,10 @@ public static function warn(string $message) : void
Log::warning($message);
}


public static function line() : void
{
ConsoleOutput::line('');
}

}
Loading

0 comments on commit 59dd574

Please sign in to comment.