ew_deployer_config/Classes/Tasks/Rsync.php
2025-04-18 14:11:54 +02:00

312 lines
9.0 KiB
PHP

<?php
declare(strict_types=1);
namespace Evoweb\DeployerConfig\Tasks;
use function Deployer\runLocally;
use function Deployer\test;
use function Deployer\testLocally;
use function Deployer\warning;
class Rsync extends AbstractTasks
{
protected array $defaultConfig = [
'exclude' => [
'.dep/repo',
'.git',
'.env',
'deploy.php',
'deploy.lock',
'current',
'shared/',
'var/*',
],
'exclude_upload' => [
'.dep/repo/',
'deploy.lock',
'current',
'shared/',
],
'exclude-file' => false,
'include' => [
'var/',
],
'include-file' => false,
'filter' => [],
'filter-file' => false,
'filter-perdir' => false,
'flags' => 'rzlc',
'options' => ['delete', 'delete-after'],
'timeout' => 600,
];
protected array $tasks = [
'rsync:warmup',
'rsync:remote',
'rsync:switch_current',
];
protected function setDefaultConfiguration(): void
{
$this->set('port', 22);
$this->set('rsync', []);
$this->set('rsync_default', $this->defaultConfig);
$this->set('rsync_dest', '{{user}}@{{hostname}}:\'{{remote_path}}/\'');
$this->set('rsync_config', $this->rsyncConfig(...));
$this->set('rsync_flags', $this->rsyncFlags(...));
$this->set('rsync_excludes', $this->rsyncExcludes(...));
$this->set('rsync_excludes_download', $this->rsyncExcludesDownload(...));
$this->set('rsync_excludes_upload', $this->rsyncExcludesUpload(...));
$this->set('rsync_includes', $this->rsyncIncludes(...));
$this->set('rsync_filter', $this->rsyncFilter(...));
$this->set('rsync_options', $this->rsyncOptions(...));
$this->set('rsync_timeout', $this->rsyncTimeout(...));
}
/**
* Warmup remote Rsync target
*/
public function warmup(): void
{
if (!testLocally('[ -d "{{deploy_path}}" ]')) {
runLocally('mkdir -p {{deploy_path}}');
}
if (testLocally('[ -d "{{deploy_path}}" ]')) {
$config = $this->get('rsync_config');
$identityFile = $this->get('identity_file') ? ' -i ' . $this->get('identity_file') : '';
runLocally(
'
rsync \
-e "ssh -p {{port}}' . $identityFile . '" \
{{rsync_flags}} \
{{rsync_options}} \
{{rsync_timeout}} \
--exclude=' . escapeshellarg('shared') . ' \
{{rsync_excludes_download}} \
{{rsync_includes}} \
{{rsync_filter}} \
{{rsync_dest}} {{deploy_path}}
',
$config
);
} else {
warning('<comment>No destination folder found.</comment>');
}
}
/**
* Rsync local->remote
*/
public function remote(): void
{
if (test('[ -d "{{remote_path}}" ]')) {
$config = $this->get('rsync_config');
$identityFile = $this->get('identity_file') ? ' -i ' . $this->get('identity_file') : '';
runLocally(
'
rsync \
-e "ssh -p {{port}}' . $identityFile . '" \
{{rsync_flags}} \
{{rsync_options}} \
{{rsync_timeout}} \
{{rsync_includes}} \
--exclude=' . escapeshellarg('shared/') . ' \
{{rsync_excludes_upload}} \
{{rsync_filter}} \
{{deploy_path}}/ {{rsync_dest}}
',
$config
);
} else {
warning('<comment>No destination folder found.</comment>');
}
}
/**
* Sync current after release was uploaded
*/
public function switchCurrent(): void
{
$config = $this->get('rsync_config');
$identityFile = $this->get('identity_file') ? ' -i ' . $this->get('identity_file') : '';
runLocally(
'
rsync \
-e "ssh -p {{port}}' . $identityFile . '" \
{{rsync_flags}} \
{{rsync_options}} \
{{deploy_path}}/current {{rsync_dest}}
',
$config
);
}
public function rsyncConfig(): array
{
$default = $this->get('rsync_default');
$config = $this->get('rsync');
return Rsync::arrayMergeRecursiveDistinct($default, $config);
}
public function rsyncFlags(): string
{
$config = $this->get('rsync_config');
$flags = $config['flags'] ?? '';
return $flags ? ' -' . $flags : '';
}
public function rsyncExcludes(): string
{
$config = $this->get('rsync_config');
$excludes = $config['exclude'] ?? [];
$excludeFile = $config['exclude-file'] ?? '';
$excludesRsync = '';
foreach ($excludes as $exclude) {
$excludesRsync .= ' --exclude=' . escapeshellarg($exclude);
}
if (
!empty($excludeFile)
&& file_exists($excludeFile)
&& is_file($excludeFile)
&& is_readable($excludeFile)
) {
$excludesRsync .= ' --exclude-from=' . escapeshellarg($excludeFile);
}
return $excludesRsync;
}
public function rsyncExcludesDownload(): string
{
$config = $this->get('rsync_config');
$excludes = $config['exclude_download'] ?? $config['exclude'] ?? [];
$excludeFile = $config['exclude-file'] ?? '';
$excludesRsync = '';
foreach ($excludes as $exclude) {
$excludesRsync .= ' --exclude=' . escapeshellarg($exclude);
}
if (
!empty($excludeFile)
&& file_exists($excludeFile)
&& is_file($excludeFile)
&& is_readable($excludeFile)
) {
$excludesRsync .= ' --exclude-from=' . escapeshellarg($excludeFile);
}
return $excludesRsync;
}
public function rsyncExcludesUpload(): string
{
$config = $this->get('rsync_config');
$excludes = $config['exclude_upload'] ?? $config['exclude'] ?? [];
$excludeFile = $config['exclude-file'] ?? '';
$excludesRsync = '';
foreach ($excludes as $exclude) {
$excludesRsync .= ' --exclude=' . escapeshellarg($exclude);
}
if (
!empty($excludeFile)
&& file_exists($excludeFile)
&& is_file($excludeFile)
&& is_readable($excludeFile)
) {
$excludesRsync .= ' --exclude-from=' . escapeshellarg($excludeFile);
}
return $excludesRsync;
}
public function rsyncIncludes(): string
{
$config = $this->get('rsync_config');
$includes = $config['include'] ?? [];
$includeFile = $config['include-file'] ?? '';
$includesRsync = '';
foreach ($includes as $include) {
$includesRsync .= ' --include=' . escapeshellarg($include);
}
if (
!empty($includeFile)
&& file_exists($includeFile)
&& is_file($includeFile)
&& is_readable($includeFile)
) {
$includesRsync .= ' --include-from=' . escapeshellarg($includeFile);
}
return $includesRsync;
}
public function rsyncFilter(): string
{
$config = $this->get('rsync_config');
$filters = $config['filter'] ?? [];
$filterFile = $config['filter-file'] ?? '';
$filterPerDir = $config['filter-perdir'] ?? '';
$filtersRsync = '';
foreach ($filters as $filter) {
$filtersRsync .= " --filter='$filter'";
}
if (!empty($filterFile)) {
$filtersRsync .= " --filter='merge $filterFile'";
}
if (!empty($filterPerDir)) {
$filtersRsync .= " --filter='dir-merge $filterPerDir'";
}
return $filtersRsync;
}
public function rsyncOptions(): string
{
$config = $this->get('rsync_config');
$options = $config['options'] ?? [];
$optionsRsync = [];
foreach ($options as $option) {
$optionsRsync[] = '--' . $option;
}
return ' ' . implode(' ', $optionsRsync);
}
public function rsyncTimeout(): string
{
$config = $this->get('rsync_config');
$timeout = $config['timeout'] ?? 0;
return $timeout ? ' --timeout=' . $timeout : '';
}
public static function arrayMergeRecursiveDistinct(array $array1, array &$array2): array
{
$merged = $array1;
foreach ($array2 as $key => &$value) {
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
$merged[$key] = self::arrayMergeRecursiveDistinct($merged[$key], $value);
} else {
$merged[$key] = $value;
}
}
return $merged;
}
}