Skip to content

Commit 59dd574

Browse files
authored
Merge pull request #1 from J-T-McC/feature/migration
Migrate feature & Laravel 7 Support
2 parents 3483629 + 75591f7 commit 59dd574

File tree

13 files changed

+215
-48
lines changed

13 files changed

+215
-48
lines changed

.github/workflows/run_tests.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ jobs:
1010
matrix:
1111
os: [ubuntu-latest]
1212
php: [7.4, 8.0]
13-
laravel: [8.*]
13+
laravel: [8.*, 7.*]
1414
dependency-version: [prefer-lowest, prefer-stable]
1515
include:
1616
- laravel: 8.*
1717
testbench: 6.*
18+
- laravel: 7.*
19+
testbench: 5.*
1820

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

README.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ The purpose of this package is to introduce local zero-downtime deployments into
55

66
## Requirements
77

8-
* Laravel 8
8+
* Laravel 7 | 8
99
* PHP ^7.4 | ^8.0
1010

1111
## Installation
@@ -64,13 +64,26 @@ return [
6464
* Max number of build directories allowed
6565
* Once limit is hit, old deployments will be removed automatically after a successful build
6666
*/
67-
'build-limit' => 10
67+
'build-limit' => 10,
68+
69+
/**
70+
* Migrate files|folders from the outgoing production build to your new release using a relative path and pattern
71+
* @see https://www.php.net/manual/en/function.glob.php
72+
*/
73+
'migrate' => [
74+
// 'storage/framework/sessions/*',
75+
]
76+
6877
];
6978
```
7079

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

83+
You might find yourself in a situation where you need to migrate files that don't exist in your build project from your
84+
current deployment folder to your new deployment folder. These files/folders can be specified in the migrate config array,
85+
and they will be copied from the outgoing deployment into the new deployment when you run the deploy command.
86+
7487
Once you have configured your env and have deployed a build, you can update your webserver to start routing traffic
7588
to your 'deployment-link' location.
7689

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

107-
Deploy current under using a custom directory name
120+
Deploy current build using a custom directory name
108121
```shell script
109122
php artisan atomic-deployments:deploy --directory=deployment_folder
110123
```

config/atomic-deployments.php

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,14 @@
2525
* Max number of build directories allowed
2626
* Once limit is hit, old deployments will be removed automatically after a successful build
2727
*/
28-
'build-limit' => 10
28+
'build-limit' => 10,
29+
30+
/**
31+
* Migrate files|folders from the outgoing production build to your new release using a relative path and pattern
32+
* @see https://www.php.net/manual/en/function.glob.php
33+
*/
34+
'migrate' => [
35+
// 'storage/framework/sessions/*',
36+
]
37+
2938
];

src/Commands/DeployCommand.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ public function handle()
2727
$buildPath = config('atomic-deployments.build-path');
2828
$deploymentLink = config('atomic-deployments.deployment-link');
2929
$deploymentsPath = config('atomic-deployments.deployments-path');
30+
$migrate = config('atomic-deployments.migrate', []);
3031

3132
$atomicDeployment = (new AtomicDeployments(
3233
$deploymentLink,
3334
$deploymentsPath,
3435
$buildPath,
36+
$migrate,
3537
$this->option('dry-run')
3638
));
3739

src/Commands/ListCommand.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ public function handle()
2525
'deployment_link',
2626
'deployment_status',
2727
'created_at',
28-
)->get()->append('isCurrentlyDeployed')->map(function($deployment) {
28+
)->get()->map(function($deployment) {
29+
$deployment->append('isCurrentlyDeployed');
2930
$deployment->deployment_status = DeploymentStatus::getNameFromValue($deployment->deployment_status);
3031
return $deployment;
3132
});

src/Models/AtomicDeployment.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
namespace JTMcC\AtomicDeployments\Models;
44

5-
use Illuminate\Database\Eloquent\Factories\HasFactory;
65
use Illuminate\Database\Eloquent\Model;
76
use Illuminate\Support\Facades\File;
87
use JTMcC\AtomicDeployments\Exceptions\AreYouInsaneException;
@@ -13,7 +12,7 @@
1312

1413
class AtomicDeployment extends Model
1514
{
16-
use HasFactory, SoftDeletes;
15+
use SoftDeletes;
1716

1817

1918
protected $fillable = [

src/Services/AtomicDeployments.php

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@
1414
use JTMcC\AtomicDeployments\Models\Enums\DeploymentStatus;
1515

1616
use Illuminate\Support\Pluralizer;
17+
use Illuminate\Support\Facades\File;
1718

1819
class AtomicDeployments
1920
{
2021

2122
protected ?AtomicDeployment $model = null;
2223

2324
protected bool $dryRun;
25+
protected array $migrate;
2426

2527
protected string $buildPath;
2628
protected string $deploymentLink;
@@ -34,15 +36,22 @@ class AtomicDeployments
3436
* @param string $deploymentLink
3537
* @param string $deploymentsPath
3638
* @param string $buildPath
39+
* @param array $migrate
3740
* @param bool $dryRun
3841
*
3942
* @throws ExecuteFailedException
4043
*/
41-
public function __construct(string $deploymentLink, string $deploymentsPath, string $buildPath, bool $dryRun = false)
44+
public function __construct(
45+
string $deploymentLink,
46+
string $deploymentsPath,
47+
string $buildPath,
48+
array $migrate = [],
49+
bool $dryRun = false)
4250
{
4351
$this->deploymentLink = $deploymentLink;
4452
$this->deploymentsPath = $deploymentsPath;
4553
$this->buildPath = $buildPath;
54+
$this->migrate = $migrate;
4655
$this->dryRun = $dryRun;
4756

4857
register_shutdown_function([$this, 'shutdown']);
@@ -57,7 +66,7 @@ public function __construct(string $deploymentLink, string $deploymentsPath, str
5766
* @param Closure|null $success
5867
* @param Closure|null $failed
5968
*/
60-
public function deploy(?Closure $success = null, ?Closure $failed = null)
69+
public function deploy(?Closure $success = null, ?Closure $failed = null): void
6170
{
6271
try {
6372

@@ -80,6 +89,7 @@ public function deploy(?Closure $success = null, ?Closure $failed = null)
8089
$this->createDeploymentDirectory();
8190
$this->confirmRequiredDirectoriesExist();
8291
$this->copyDeploymentContents();
92+
$this->copyMigrationContents();
8393
$this->linkDeployment($this->deploymentLink, $this->deploymentPath);
8494
$this->confirmSymbolicLink($this->deploymentPath);
8595
$this->updateDeploymentStatus(DeploymentStatus::SUCCESS);
@@ -104,7 +114,7 @@ public function deploy(?Closure $success = null, ?Closure $failed = null)
104114
*
105115
* @param int $status
106116
*/
107-
public function updateDeploymentStatus(int $status)
117+
public function updateDeploymentStatus(int $status): void
108118
{
109119
if ($this->isDryRun()) {
110120
Output::warn('Dry run - Skipping deployment status update');
@@ -132,7 +142,7 @@ public function updateDeploymentStatus(int $status)
132142
*
133143
* @throws ExecuteFailedException
134144
*/
135-
public function confirmSymbolicLink(string $deploymentPath)
145+
public function confirmSymbolicLink(string $deploymentPath): bool
136146
{
137147
Output::info('Confirming deployment link is correct');
138148
$currentDeploymentPath = $this->getCurrentDeploymentPath();
@@ -154,7 +164,7 @@ public function confirmSymbolicLink(string $deploymentPath)
154164
/**
155165
* @throws InvalidPathException
156166
*/
157-
public function confirmRequiredDirectoriesExist()
167+
public function confirmRequiredDirectoriesExist(): void
158168
{
159169
if ($this->isDryRun()) {
160170
Output::warn('Dry run - Skipping required directory exists check for:');
@@ -176,7 +186,7 @@ public function createDeploymentDirectory(): void
176186
{
177187
Output::info("Creating directory at {$this->deploymentPath}");
178188

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

@@ -204,11 +214,63 @@ public function copyDeploymentContents(): void
204214
return;
205215
}
206216

207-
Exec::rsyncDir("{$this->buildPath}/", "{$this->deploymentPath}/");
217+
Exec::rsync("{$this->buildPath}/", "{$this->deploymentPath}/");
208218
Output::info('Copying complete');
209219
}
210220

211221

222+
/**
223+
* @throws ExecuteFailedException
224+
*/
225+
public function copyMigrationContents(): void
226+
{
227+
if (!empty($this->initialDeploymentPath) && count($this->migrate)) {
228+
229+
if ($this->isDryRun()) {
230+
Output::warn('Dry run - skipping migrations');
231+
}
232+
233+
collect($this->migrate)->each(function ($pattern) {
234+
235+
if (!$this->isDryRun()) {
236+
Output::info("Running migration for pattern {$pattern}");
237+
}
238+
239+
$rootFrom = rtrim($this->initialDeploymentPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
240+
$rootTo = rtrim($this->deploymentPath, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR;
241+
242+
foreach (File::glob($rootFrom . $pattern) as $from) {
243+
244+
$dir = $from;
245+
246+
if (!File::isDirectory($dir)) {
247+
$dir = File::dirname($dir);
248+
}
249+
250+
$dir = str_replace($rootFrom, $rootTo, $dir);
251+
$to = str_replace($rootFrom, $rootTo, $from);
252+
253+
if ($this->isDryRun()) {
254+
Output::warn("Dry run - migrate: \r\n - {$from}\r\n - {$to}");
255+
Output::line();
256+
continue;
257+
}
258+
259+
File::ensureDirectoryExists($dir, 0755, true);
260+
261+
Exec::rsync($from, $to);
262+
263+
}
264+
265+
if (!$this->isDryRun()) {
266+
Output::info("Finished migration for pattern {$pattern}");
267+
}
268+
269+
});
270+
}
271+
}
272+
273+
212274
/**
213275
* Create Symbolic link for live deployment
214276
* Will overwrite previous link
@@ -249,10 +311,10 @@ public function setDeploymentDirectory(string $name): void
249311
*
250312
* @throws ExecuteFailedException
251313
*/
252-
public function getCurrentDeploymentPath()
314+
public function getCurrentDeploymentPath(): string
253315
{
254316
$result = Exec::readlink($this->deploymentLink);
255-
if($result === $this->deploymentLink) {
317+
if ($result === $this->deploymentLink) {
256318
return '';
257319
}
258320
return $result;
@@ -276,7 +338,7 @@ public function setDeploymentPath(): void
276338
*
277339
* @see getCurrentDeploymentPath() to get the path currently in use
278340
*/
279-
public function getDeploymentPath()
341+
public function getDeploymentPath(): string
280342
{
281343
return $this->deploymentsPath;
282344
}
@@ -333,7 +395,7 @@ public function rollback(): void
333395
* @throws ExecuteFailedException
334396
* @throws InvalidPathException
335397
*/
336-
public function cleanBuilds($limit)
398+
public function cleanBuilds($limit): void
337399
{
338400
Output::alert('Running Build Cleanup');
339401
Output::info("Max deployment directories allowed set to {$limit}");
@@ -368,23 +430,30 @@ public function cleanBuilds($limit)
368430
}
369431

370432

371-
public function isDryRun() {
433+
/**
434+
* @return bool
435+
*/
436+
public function isDryRun(): bool
437+
{
372438
return $this->dryRun;
373439
}
374-
440+
375441

376442
/**
377443
* @throws ExecuteFailedException
378444
*/
379-
public function failed()
445+
public function failed(): void
380446
{
381447
$this->rollback();
382448
$this->updateDeploymentStatus(DeploymentStatus::FAILED);
383449
DeploymentFailed::dispatch($this, $this->model);
384450
}
385451

386-
387-
public function shutdown()
452+
453+
/**
454+
* @throws ExecuteFailedException
455+
*/
456+
public function shutdown(): void
388457
{
389458
if ($error = error_get_last()) {
390459
Output::error("Error detected during shutdown, requesting rollback");

src/Services/Exec.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,27 @@ public static function ln(string $link, string $path)
7373
*
7474
* @throws ExecuteFailedException
7575
*/
76-
public static function rsyncDir(string $from, string $to) {
76+
public static function rsync(string $from, string $to) {
7777
return self::run('rsync -aW --no-compress %s %s', [$from, $to]);
7878
}
7979

8080

81+
/**
82+
* @param string $from
83+
* @param string $to
84+
* @param string $pattern
85+
*
86+
* @return string
87+
*
88+
* @throws ExecuteFailedException
89+
*/
90+
public static function rsyncPattern(string $from, string $to, string $pattern) {
91+
$from = rtrim($from, DIRECTORY_SEPARATOR ) . '/';
92+
$to = rtrim($to, DIRECTORY_SEPARATOR) . '/';
93+
return self::run('rsync -aW --no-compress --include="'.$pattern.'" --exclude="*" %s %s', [$from, $to]);
94+
}
95+
96+
8197
/**
8298
* @return string
8399
*

src/Services/Output.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
class Output
1010
{
1111

12-
1312
/**
1413
* Print throwable to console | log
1514
*
@@ -83,4 +82,10 @@ public static function warn(string $message) : void
8382
Log::warning($message);
8483
}
8584

85+
86+
public static function line() : void
87+
{
88+
ConsoleOutput::line('');
89+
}
90+
8691
}

0 commit comments

Comments
 (0)