Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft. Updating Drupal database copy tasks to better support copying from one server to another. #30

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ Please note that tasks assume databases on relevant stages have already been
configured. If you need to skip all database operations, you can set
`skip_db_ops` to `true` [via the command line](https://deployer.org/docs/7.x/cli#overriding-configuration-options).

Run `vendor/bin/dep tree deploy` to view the `deploy` recipe tree.
Run `vendor/bin/dep tree deploy` to view the `deploy` task tree.

Run `vendor/bin/dep deploy` to deploy.

Run `vendor/bin/dep` to review available recipes.
Run `vendor/bin/dep` to review available tasks.

### Before/After Hooks
Deployer supports running tasks before or after other defined tasks. Defining
Expand Down
6 changes: 4 additions & 2 deletions cms/drupal/8.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
]);
fill('shared_file_names', [
'.env',
'.env.local',
// @todo remove the following three lines once confirmed no longer necessary
'{{app_directory_name}}/sites/{{site}}/config.local.php',
'{{app_directory_name}}/sites/{{site}}/databases.local.php',
'{{app_directory_name}}/sites/{{site}}/settings.local.php',
Expand All @@ -38,10 +40,9 @@
config_backup();
import('recipe/drupal8.php');
config_backup_merge();
import('vendor/unleashedtech/deployer-recipes/db/backup/download.php');
import('vendor/unleashedtech/deployer-recipes/cms/drupal/db/backup/create.php');
import('vendor/unleashedtech/deployer-recipes/cms/drupal/db/backup/import.php');
import('vendor/unleashedtech/deployer-recipes/cms/drupal/db/pull.yml');
import('vendor/unleashedtech/deployer-recipes/cms/drupal/db/copy.php');
import('vendor/unleashedtech/deployer-recipes/cms/drupal/vars/vars.yml');
import('vendor/unleashedtech/deployer-recipes/cms/drupal/post/deploy.yml');
import('vendor/unleashedtech/deployer-recipes/cms/drupal/pre/deploy.yml');
Expand All @@ -50,6 +51,7 @@

// Create the "deploy" task.
task('deploy', [
'cms:drupal:db:backup:create',
'cms:drupal:db:backup:create',
'cms:drupal:vars',
'deploy:prepare',
Expand Down
66 changes: 50 additions & 16 deletions cms/drupal/db/backup/copy.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,64 @@

namespace Deployer;

use UnleashedTech\DeployerRecipes\VirtualMachine\VirtualMachine;
use Deployer\Host\Host;

task('cms:drupal:db:backup:copy', static function (): void {
// Get the destination host.
$host = get('backup_destination_host');
if (! $host) {
throw new \UnexpectedValueException('Backup destination host required.');
// Get the environments the file(s) should be copied to.
$environments = get('environments_copy_db_files');
if (! \count($environments)) {
error('Destination environments required.');
}

// Copy it to the destination host.
if (! \in_array($host, VirtualMachine::getNames(), true)) {
// TODO: transfer it to the destination host
throw new \UnexpectedValueException('Host to host file transfer is not supported yet.');
}

// TODO: transfer directly into the VM via scp instead of using runLocally
runLocally('mkdir -p {{local_database_backups}}');
// Get the list of files to copy.
$files = [];
$sites = get('sites');
if (! \is_array($sites)) {
$sites = \explode(',', $sites);
}

foreach ($sites as $site) {
$file = run('\ls -rt -1 {{backups}} | grep "' . $site . '" | tail -1');
download('{{backups}}/' . $file, '{{local_database_backups}}/');
$files[] = run('\ls -rt -1 {{backups}} | grep "' . $site . '" | tail -1');
}

// Copy the files to the destination environment(s).
foreach ($environments as $environment) {
if ($environment === 'local') {
// TODO: transfer directly into the VM via scp instead of using runLocally
runLocally('mkdir -p {{local_database_backups}}');
foreach ($files as $file) {
download('{{backups}}/' . $file, '{{local_database_backups}}/');
}
} else {
$currentHost = currentHost();
foreach (Deployer::get()->hosts as $destHost) {
\assert($destHost instanceof Host);
if ($environment !== $destHost->getLabels()['stage']) {
continue;
}

foreach ($files as $file) {
$sourceFilename = '{{backups}}/' . $file;
$destFilename = '{{backups}}/' . $file;
// Only copy the file if it does not already exist at the destination.
$exists = (bool) runLocally(\vsprintf('ssh %s [ -f %s ] && echo 1 || echo 0', [
$destHost->getHostname(),
$destFilename,
]));
if ($exists) {
continue;
}

info(\vsprintf('Copying "%s" to "%s".', [
$file,
$destHost->getHostname(),
]));
runLocally(\vsprintf('scp -3 %s %s', [
$currentHost->getHostname() . ':' . $sourceFilename,
$destHost->getHostname() . ':' . $destFilename,
]));
}
}
}
}
})->desc('Download the latest database backup.')->once();
})->desc('Copy the latest database backup(s).')->once();
15 changes: 3 additions & 12 deletions cms/drupal/db/backup/destination.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,14 @@
use UnleashedTech\DeployerRecipes\VirtualMachine\VirtualMachine;

task('cms:drupal:db:backup:destination', static function (): void {
// Build the list of host choices.
$choices = [
$backupDestinationHostOptions = [
VirtualMachine::load('drupal')->getName(),
];
$protected = get('environments_protected');
if (! \is_array($protected)) {
$protected = \explode(',', $protected);
}

foreach (Deployer::get()->hosts as $host) {
/** @var Host $host */
$stage = $host->getLabels()['stage'];
if (! \in_array($stage, $protected, true)) {
$choices[] = $host->getHostname();
}
$backupDestinationHostOptions[] = $host->getHostname();
}

// Prompt which host the db backup should be imported on.
set('backup_destination_host', askChoice('Which host should the exported database(s) be imported on?', $choices, 0));
set('backup_destination_host', askChoice('Which host should the exported database(s) be copied to?', $backupDestinationHostOptions, 0));
})->desc('Prompt for the database backup destination host.')->once();
64 changes: 64 additions & 0 deletions cms/drupal/db/copy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?php

/**
* Prompts for the database backup destination host.
*
* @file
*/

declare(strict_types=1);

namespace Deployer;

use Deployer\Host\Host;

task('cms:drupal:db:copy', static function (): void {
import('vendor/unleashedtech/deployer-recipes/cms/drupal/db/backup/create.php');
import('vendor/unleashedtech/deployer-recipes/cms/drupal/db/backup/copy.php');
import('vendor/unleashedtech/deployer-recipes/cms/drupal/db/backup/import.php');

// Prompt which environment(s) the db backup(s) should be copied to.
$destEnvironmentsOptions = [];
foreach (Deployer::get()->hosts as $host) {
\assert($host instanceof Host);
if (! $host->has('labels')) {
continue;
}

$environment = $host->getLabels()['stage'];
if ($environment !== currentHost()->getLabels()['stage']) {
$destEnvironmentsOptions[] = $environment;
}
}

$destEnvironments = askChoice('Which environment(s) should the exported database(s) be copied to? (comma separated)', \array_unique($destEnvironmentsOptions), 0, true);
set('environments_copy_db_files', $destEnvironments);

// Prompt whether the db backup(s) should be imported.
$protectedEnvironments = get('environments_protected');
if (! \is_array($protectedEnvironments)) {
$protectedEnvironments = \explode(',', $protectedEnvironments ?? '');
}

$environmentsToImportDbFiles = [];
foreach ($destEnvironments as $destEnvironment) {
if (\in_array($destEnvironment, $protectedEnvironments, true)) {
info(\vsprintf('Since the "%s" environment is protected, I won\'t ask about importing the DB there...', [
$destEnvironment,
]));
} else {
$response = askChoice(\vsprintf('Would you like to import the database file(s) once copied to the "%s" environment?', [
$destEnvironment,
]), ['No', 'Yes'], 0);
if ($response === 'Yes') {
$environmentsToImportDbFiles[] = $destEnvironment;
}
}
}

set('environments_import_db_files', $environmentsToImportDbFiles);

// Run the related tasks.
invoke('cms:drupal:db:backup:create');
invoke('cms:drupal:db:backup:copy');
})->desc('Prompt for the database backup destination host.')->once();
11 changes: 0 additions & 11 deletions cms/drupal/db/pull.yml

This file was deleted.

1 change: 1 addition & 0 deletions cms/drupal/db/update.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

foreach ($sites as $site) {
within($appPath . '/sites/' . $site, static function () use ($timeout): void {
// TODO: this task should immediately fail if one of the db updates fails (e.g. it shouldn't continue to the next site)
run('{{drush}} updb -y', [], $timeout);
});
}
Expand Down
5 changes: 4 additions & 1 deletion cms/wp/5.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
// Conditionally apply WordPress-specific defaults.
set('app_type', 'wordpress');
fill('upload_dir', 'web/app/uploads');
fill('shared_files', ['.env']);
fill('shared_files', [
'.env',
'.env.local',
]);
fill('shared_dirs', ['{{upload_dir}}']);
fill('writable_dirs', ['{{upload_dir}}']);
fill('wp', '{{bin}}/wp');
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"source": "https://github.com/unleashedtech/deployer-recipes"
},
"require": {
"php": "^7.3 || ^8.0",
"php": "^7.4 || ^8.0",
"deployer/dist": "7.0.0-rc.4"
},
"minimum-stability": "dev",
Expand Down
1 change: 1 addition & 0 deletions config.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
require_once __DIR__ . '/src/functions.php';

import('vendor/unleashedtech/deployer-recipes/dev/sites.php');
import('vendor/unleashedtech/deployer-recipes/db/backup/copy.php');

// Conditionally apply global defaults.
fill('app_path', '{{release}}/{{app_directory_name}}');
Expand Down
23 changes: 23 additions & 0 deletions db/backup/copy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

/**
* Copy database(s) to various environments.
*
* @file
*/

declare(strict_types=1);

namespace Deployer;

task('db:copy', static function (): void {
$app_type = get('app_type');
switch($app_type) {
case 'drupal':
invoke(\sprintf('cms:%s:db:copy', $app_type));
break;

default:
throw new \DomainException(\sprintf('Unsupported app type: %s', $app_type));
}
})->desc('Copy database(s) to various environments.')->once();
10 changes: 10 additions & 0 deletions env/check.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

declare(strict_types=1);

namespace Deployer;

task('env:check', static function (): void {
// @todo confirm target environment complies with composer-defined requirements
// (not only check for correct PHP version via CLI, but also via wget?)
});
11 changes: 10 additions & 1 deletion src/VirtualMachine/ClientInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,14 @@ public function getClientTimeout(int $default = 3600): int;

public function import(string $file): string;

public function run(string $command): string;
// @todo remove the following phpcs directive once PHP 7.3 is no longer supported
// phpcs:disable SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue.NullabilityTypeMissing

/**
* @param string[] $options
*/
public function run(string $command, array $options = [], int $timeout = null): string;

// @todo remove the following phpcs directive once PHP 7.3 is no longer supported
// phpcs:enable SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue.NullabilityTypeMissing
}
11 changes: 10 additions & 1 deletion src/VirtualMachine/Ddev/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,20 @@ public function getName(): string
return 'ddev';
}

public function run(string $command): string
// @todo remove the following phpcs directive once PHP 7.3 is no longer supported
// phpcs:disable SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue.NullabilityTypeMissing

/**
* @param string[] $options
*/
public function run(string $command, array $options = [], int $timeout = null): string
{
return runLocally(\sprintf('ddev exec "%s"', $command));
}

// @todo remove the following phpcs directive once PHP 7.3 is no longer supported
// phpcs:enable SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue.NullabilityTypeMissing

public function import(string $file): string
{
// TODO: Implement import() method.
Expand Down
4 changes: 1 addition & 3 deletions src/VirtualMachine/Docksal/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ public function getName(): string
// phpcs:disable SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue.NullabilityTypeMissing

/**
* @param string[]|null $options
*
* @throws \Deployer\Exception\RunException
* @param string[] $options
*/
public function run(string $command, array $options = [], int $timeout = null): string
{
Expand Down
11 changes: 10 additions & 1 deletion src/VirtualMachine/Vagrant/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,20 @@ public function getName(): string
return 'vagrant';
}

public function run(string $command): string
// @todo remove the following phpcs directive once PHP 7.3 is no longer supported
// phpcs:disable SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue.NullabilityTypeMissing

/**
* @param string[] $options
*/
public function run(string $command, array $options = [], int $timeout = null): string
{
return runLocally(\sprintf('vagrant ssh -c "%s"', $command));
}

// @todo remove the following phpcs directive once PHP 7.3 is no longer supported
// phpcs:enable SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue.NullabilityTypeMissing

public function import(string $file): string
{
// TODO: Implement import() method.
Expand Down
3 changes: 2 additions & 1 deletion src/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ function fill(string $var, $defaultValue): void

case 'boolean':
case 'int':
// Var already defined. Do nothing.
case 'NULL':
// Var already explicitly defined. Do nothing.
break;

default:
Expand Down