From 5151b22dc26307d46f7a8aa3f45e706086edc08b Mon Sep 17 00:00:00 2001 From: Sebastian Fischer Date: Fri, 18 Apr 2025 14:11:54 +0200 Subject: [PATCH] [TASK] Cleanup and improve some options --- Classes/Config/Deployment.php | 33 +++++------ Classes/Tasks/AbstractTasks.php | 23 -------- Classes/Tasks/Deploy.php | 11 ++-- Classes/Tasks/Local.php | 65 ++++++++------------ Classes/Tasks/Remote.php | 26 +++++++- Classes/Tasks/Rsync.php | 60 ++++++++++--------- README.md | 101 +++++++++++--------------------- alias.sh | 2 +- deploy.php | 2 +- 9 files changed, 137 insertions(+), 186 deletions(-) diff --git a/Classes/Config/Deployment.php b/Classes/Config/Deployment.php index baab032..592dab6 100644 --- a/Classes/Config/Deployment.php +++ b/Classes/Config/Deployment.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace Evoweb\DeployerConfig\Config; -use Deployer\Deployer; use Deployer\Exception\Exception; use Evoweb\DeployerConfig\Tasks\Deploy; use Evoweb\DeployerConfig\Tasks\Local; @@ -17,38 +16,37 @@ use function Deployer\import; use function Deployer\parse; use function Deployer\runLocally; use function Deployer\set; -use function Deployer\Support\str_contains; use function Deployer\testLocally; use function Deployer\warning; class Deployment { + protected array $environmentVariables = [ + 'CI_PROJECT_DIR', + 'CI_HOST', + 'ENVIRONMENT', + 'INSTANCE_ID' + ]; + public function __construct() { $this->loadDeployerCommon(); $this->setDefaultConfiguration(); $this->loadHostConfiguration(); $this->registerTasks(); - } protected function loadDeployerCommon(): void { - foreach (['/../../', '/../../../', '/../../../../'] as $path) { - $file = realpath(__DIR__ . $path . 'vendor/deployer/deployer/recipe/common.php'); - if ($file && file_exists($file)) { - require $file; - break; - } + $file = realpath(__DIR__ . '/../../../../deployer/deployer/recipe/common.php'); + if ($file && file_exists($file)) { + require $file; } } protected function setDefaultConfiguration(): void { - $this->set('CI_PROJECT_DIR', getEnv('CI_PROJECT_DIR')); - $this->set('CI_HOST', getEnv('CI_HOST')); - $this->set('ENVIRONMENT_NAME', getEnv('ENVIRONMENT_NAME')); - $this->set('INSTANCE_ID', getEnv('INSTANCE_ID')); + array_walk($this->environmentVariables, fn ($variable) => $this->set($variable, getEnv($variable))); $this->set('app_container_path', ''); @@ -195,12 +193,9 @@ class Deployment protected function loadHostConfiguration(): void { // read configuration - foreach (['/../../../../', '/../../../../../'] as $path) { - $file = realpath(__DIR__ . $path . $_ENV['ENVIRONMENT_NAME'] . '.yaml'); - if (file_exists($file)) { - import($file); - break; - } + $file = realpath(__DIR__ . '/../../../../../hosts.yaml'); + if ($file && file_exists($file)) { + import($file); } } diff --git a/Classes/Tasks/AbstractTasks.php b/Classes/Tasks/AbstractTasks.php index 16935a9..e435bc0 100644 --- a/Classes/Tasks/AbstractTasks.php +++ b/Classes/Tasks/AbstractTasks.php @@ -15,7 +15,6 @@ use ReflectionMethod; use function Deployer\get; use function Deployer\has; use function Deployer\set; -use function Deployer\test; use function Deployer\testLocally; abstract class AbstractTasks @@ -102,26 +101,4 @@ abstract class AbstractTasks { return $this->has($name) && is_array($this->get($name)); } - - protected function getApplicationContext(): string - { - return $this->has('application_context') ? - 'TYPO3_CONTEXT="' . $this->get('application_context') . '" ' : - ''; - } - - protected function getSudo(): string - { - return $this->get('clear_use_sudo') ? 'sudo ' : ''; - } - - protected function getTYPO3Bin(string $path, string $testPath): string - { - if (test('[ -f ' . $testPath . '/releases/{{release_name}}/vendor/bin/typo3cms ]')) { - $bin = $path . '/releases/{{release_name}}/vendor/bin/typo3cms'; - } else { - $bin = $path . '/releases/{{release_name}}/vendor/bin/typo3'; - } - return $bin; - } } diff --git a/Classes/Tasks/Deploy.php b/Classes/Tasks/Deploy.php index 681fd2a..98065b7 100644 --- a/Classes/Tasks/Deploy.php +++ b/Classes/Tasks/Deploy.php @@ -21,7 +21,6 @@ class Deploy extends AbstractTasks 'local:shared', 'local:writable', 'local:write_release', - 'local:create_folder', 'local:vendors', ], @@ -41,17 +40,21 @@ class Deploy extends AbstractTasks 'local:unlock', 'deploy:success', ], + + 'deploy' => [ + 'deploy:prepare', + 'deploy:publish', + ] ]; - public function __construct() + protected function initializeTasks(): void { - parent::__construct(); + array_walk($this->tasks, [$this, 'registerTask']); $this->registerAfterTask(); } protected function registerAfterTask(): void { - // [Optional] if deploy fails automatically unlock. after('deploy:failed', 'local:unlock'); } } diff --git a/Classes/Tasks/Local.php b/Classes/Tasks/Local.php index b01e7f4..9251d73 100644 --- a/Classes/Tasks/Local.php +++ b/Classes/Tasks/Local.php @@ -34,7 +34,6 @@ class Local extends AbstractTasks 'local:shared', 'local:writable', 'local:write_release', - 'local:create_folder', 'local:vendors', 'local:clear_paths', 'local:symlink', @@ -88,15 +87,6 @@ class Local extends AbstractTasks } } - /** - * Unlocks deploy - */ - public function unlock(): void - { - // always success - runLocally("rm -f {{deploy_path}}/.dep/deploy.lock"); - } - /** * Prepares release */ @@ -313,21 +303,23 @@ class Local extends AbstractTasks throw new \RuntimeException('Absolute path not allowed in config parameter `writable_dirs`.'); } + $options = ['cwd' => $this->get('release_or_current_path')]; + // Create directories if they don't exist - runLocally("cd {{release_or_current_path}}; mkdir -p $dirs"); + runLocally("mkdir -p $dirs", $options); if ($mode === 'chown') { $httpUser = get('http_user'); // Change owner. // -L traverse every symbolic link to a directory encountered - runLocally("cd {{release_or_current_path}}; $sudo chown -L $recursive $httpUser $dirs"); + runLocally("$sudo chown -L $recursive $httpUser $dirs", $options); } elseif ($mode === 'chgrp') { // Change group ownership. // -L traverse every symbolic link to a directory encountered - runLocally("cd {{release_or_current_path}}; $sudo chgrp -L $recursive {{http_group}} $dirs"); - runLocally("cd {{release_or_current_path}}; $sudo chmod $recursive g+rwx $dirs"); + runLocally("$sudo chgrp -L $recursive {{http_group}} $dirs", $options); + runLocally("$sudo chmod $recursive g+rwx $dirs", $options); } elseif ($mode === 'chmod') { - runLocally("cd {{release_or_current_path}}; $sudo chmod $recursive {{writable_chmod_mode}} $dirs"); + runLocally("$sudo chmod $recursive {{writable_chmod_mode}} $dirs", $options); } elseif ($mode === 'acl') { $remoteUser = get('remote_user', false); if (empty($remoteUser)) { @@ -337,7 +329,7 @@ class Local extends AbstractTasks if (strlen(runLocally("chmod --help | grep ugoa; true")) > 0) { // Try OS-X specific setting of access-rights - runLocally("cd {{release_or_current_path}}; $sudo chmod g+w $dirs"); + runLocally("$sudo chmod g+w $dirs", $options); } elseif ($this->commandExist('setfacl')) { $setFaclUsers = "-m u:\"$httpUser\":rwX"; // Check if remote user exists, before adding it to setfacl @@ -353,16 +345,16 @@ class Local extends AbstractTasks $writeableDirs = get('writable_dirs'); foreach ($writeableDirs as $dir) { // Check if ACL has been set or not - $hasfacl = runLocally("cd {{release_or_current_path}}; getfacl -p $dir | grep \"^user:$httpUser:.*w\" | wc -l"); + $hasfacl = runLocally("getfacl -p $dir | grep \"^user:$httpUser:.*w\" | wc -l", $options); // Set ACL for directory if it has not been set before if (!$hasfacl) { - runLocally("cd {{release_or_current_path}}; setfacl -L $recursive $setFaclUsers $dir"); - runLocally("cd {{release_or_current_path}}; setfacl -dL $recursive $setFaclUsers $dir"); + runLocally("setfacl -L $recursive $setFaclUsers $dir", $options); + runLocally("setfacl -dL $recursive $setFaclUsers $dir", $options); } } } else { - runLocally("cd {{release_or_current_path}}; $sudo setfacl -L $recursive $setFaclUsers $dirs"); - runLocally("cd {{release_or_current_path}}; $sudo setfacl -dL $recursive $setFaclUsers $dirs"); + runLocally("$sudo setfacl -L $recursive $setFaclUsers $dirs", $options); + runLocally("$sudo setfacl -dL $recursive $setFaclUsers $dirs", $options); } } else { $alias = currentHost()->getAlias(); @@ -371,12 +363,12 @@ class Local extends AbstractTasks } elseif ($mode === 'sticky') { // Changes the group of the files, sets sticky bit to the directories // and add the writable bit for all files - runLocally("cd {{release_or_current_path}}; for dir in $dirs;" . + runLocally("for dir in $dirs;" . 'do ' . 'chgrp -L -R {{http_group}} ${dir}; ' . 'find ${dir} -type d -exec chmod g+rwxs \{\} \;;' . 'find ${dir} -type f -exec chmod g+rw \{\} \;;' . - 'done'); + 'done', $options); } elseif ($mode === 'skip') { // Does nothing, saves time if no changes are required for some environments return; @@ -390,24 +382,6 @@ class Local extends AbstractTasks runLocally('echo {{target}} > {{release_path}}/release'); } - /** - * Creating necessary folders - */ - public function createFolder(): void - { - $sudo = $this->getSudo(); - - if (testLocally('[ ! -d {{release_path}}/var/cache ]')) { - runLocally('mkdir -p {{release_path}}/var/cache'); - } - runLocally($sudo . ' chmod -R 775 {{release_path}}/var/cache'); - - if (testLocally('[ ! -d {{release_path}}/var/log ]')) { - runLocally('mkdir -p {{release_path}}/var/log'); - } - runLocally($sudo . ' chmod -R 775 {{release_path}}/var/log'); - } - /** * Installing vendors */ @@ -483,4 +457,13 @@ class Local extends AbstractTasks { info('Folder of this release is: {{release_path}}'); } + + /** + * Unlocks deploy + */ + public function unlock(): void + { + // always success + runLocally("rm -f {{deploy_path}}/.dep/deploy.lock"); + } } diff --git a/Classes/Tasks/Remote.php b/Classes/Tasks/Remote.php index e8c0a3d..cc12fae 100644 --- a/Classes/Tasks/Remote.php +++ b/Classes/Tasks/Remote.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Evoweb\DeployerConfig\Tasks; +use function Deployer\currentHost; use function Deployer\get; use function Deployer\parse; use function Deployer\run; @@ -158,8 +159,29 @@ class Remote extends AbstractTasks */ public function clearOpcache(): void { - $webDomain = rtrim($this->get('web_domain'), '/'); $htaccess = $this->has('htaccess') ? '--user ' . $this->get('htaccess') : ''; - run('curl ' . $htaccess . ' -sk ' . $webDomain . '/cache.php'); + run('curl ' . $htaccess . ' -sk https://' . currentHost()->getAlias() . '/cache.php'); + } + + protected function getSudo(): string + { + return $this->get('clear_use_sudo') ? 'sudo ' : ''; + } + + protected function getApplicationContext(): string + { + return $this->has('application_context') ? + 'TYPO3_CONTEXT="' . $this->get('application_context') . '" ' : + ''; + } + + protected function getTYPO3Bin(string $path, string $testPath): string + { + if (test('[ -f ' . $testPath . '/releases/{{release_name}}/vendor/bin/typo3cms ]')) { + $bin = $path . '/releases/{{release_name}}/vendor/bin/typo3cms'; + } else { + $bin = $path . '/releases/{{release_name}}/vendor/bin/typo3'; + } + return $bin; } } diff --git a/Classes/Tasks/Rsync.php b/Classes/Tasks/Rsync.php index 8e2c5ac..100579d 100644 --- a/Classes/Tasks/Rsync.php +++ b/Classes/Tasks/Rsync.php @@ -55,8 +55,6 @@ class Rsync extends AbstractTasks $this->set('rsync_default', $this->defaultConfig); - $this->set('rsync_src', '{{deploy_path}}'); - $this->set('rsync_dest', '{{user}}@{{hostname}}:\'{{remote_path}}/\''); $this->set('rsync_config', $this->rsyncConfig(...)); @@ -87,18 +85,22 @@ class Rsync extends AbstractTasks } 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}}' + ' + 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('No destination folder found.'); @@ -110,20 +112,22 @@ class Rsync extends AbstractTasks */ public function remote(): void { - $config = $this->get('rsync_config'); - $identityFile = $this->get('identity_file') ? ' -i ' . $this->get('identity_file') : ''; 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}} \ - \'{{rsync_src}}/\' {{user}}@{{hostname}}:\'{{remote_path}}/\'', + ' + 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 { @@ -139,11 +143,13 @@ class Rsync extends AbstractTasks $config = $this->get('rsync_config'); $identityFile = $this->get('identity_file') ? ' -i ' . $this->get('identity_file') : ''; runLocally( - 'rsync \ - -e \'ssh -p {{port}}' . $identityFile . '\' \ + ' + rsync \ + -e "ssh -p {{port}}' . $identityFile . '" \ {{rsync_flags}} \ {{rsync_options}} \ - \'{{rsync_src}}/current\' {{user}}@{{hostname}}:\'{{remote_path}}/\'', + {{deploy_path}}/current {{rsync_dest}} + ', $config ); } diff --git a/README.md b/README.md index 878ca35..6255090 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ function composer() { --env SSH_AUTH_SOCK=/ssh-agent \ --env CI_HOST \ --env CI_PROJECT_DIR \ - --env ENVIRONMENT_NAME \ + --env ENVIRONMENT \ --env INSTANCE_ID \ --env ADDITIONAL_CONFIG_FILE \ --env TYPO3_CONTEXT \ @@ -75,9 +75,9 @@ _ARGS := $(wordlist 2, $(words $(MAKECMDGOALS)), $(MAKECMDGOALS)) # ...and turn them into do-nothing targets $(eval $(_ARGS):;@:) -production-%: ENVIRONMENT_NAME=production +production-%: ENVIRONMENT=production production-%: TYPO3_CONTEXT=Production -staging-%: ENVIRONMENT_NAME=staging +staging-%: ENVIRONMENT=staging staging-%: TYPO3_CONTEXT=Production/Staging MKFILE_PATH := $(realpath $(firstword $(MAKEFILE_LIST))) @@ -103,11 +103,11 @@ latest-tag: .PHONY: --release --release: - echo "Release for $(ENVIRONMENT_NAME) with '$(TASK)' started" + echo "Release for $(ENVIRONMENT) with '$(TASK)' started" source $(ALIAS_FILE); \ - ENVIRONMENT_NAME="$(ENVIRONMENT_NAME)" \ - composer --working-dir=$(BUILD_DIR) exec dep -- $(VERBOSE) --file=$(DEPLOY_FILE) $(TASK) - echo "Release for $(ENVIRONMENT_NAME) with '$(TASK)' finished" + ENVIRONMENT="$(ENVIRONMENT)" \ + composer --working-dir=$(BUILD_DIR) exec dep -- $(VERBOSE) --file=$(DEPLOY_FILE) $(TASK) environment=$(ENVIRONMENT) + echo "Release for $(ENVIRONMENT) with '$(TASK)' finished" ##@ ##@ Commands to release tag or branch @@ -139,68 +139,26 @@ Deploy tag 1.0.0 to production make production-release 1.0.0 ``` -## Overriding default values per host in production.yaml +## Overriding default values per host in hosts.yaml ```yaml -# for more settings look in https://github.com/deployphp/deployer/recipe/common.php -config: - bin/php: 'docker exec -e TYPO3_CONTEXT="{{application_context}}" $(docker ps -q -f name={{INSTANCE_ID}}-php-fpm-1) php' - http_user: sebastian - http_group: www-data - keep_releases: 10 - - repository: git@gitea.fischer.im:evoWeb/website_evoweb_de.git - composer_options: '{{composer_action}} --verbose --prefer-dist --no-progress --no-interaction --no-dev --optimize-autoloader --no-suggest --ignore-platform-reqs' - - prepare_dirs: - - .dep - - releases - - shared/fileadmin - - shared_dirs: - - fileadmin - - writable_recursive: true - writable_dirs: - - public/typo3temp - - var - - rsync: - exclude_download: - - 'current' - - 'shared/' - - 'var/*' - - '-,p public/typo3temp' - - remote_path: /srv/{{INSTANCE_ID}}/data/htdocs/production - app_container_path: /usr/local/apache2/htdocs/production - application_context: Production - web_domain: https://www.evoweb.de/ - - -hosts: - production: - remote_user: sebastian - hostname: '{{CI_HOST}}' - deploy_path: '{{CI_PROJECT_DIR}}/Build/cache/{{ENVIRONMENT_NAME}}' - identity_file: ~/.ssh/rsync_id_ed25519 -``` - -## Overriding default values per host in staging.yaml -```yaml -# for more settings look in https://github.com/deployphp/deployer/recipe/common.php +# for more settings, look in https://github.com/deployphp/deployer/recipe/common.php config: bin/php: 'docker exec -e TYPO3_CONTEXT="{{application_context}}" $(docker ps -q -f name={{INSTANCE_ID}}-php-fpm-1) php' http_user: sebastian http_group: www-data keep_releases: 5 + remote_user: sebastian + identity_file: ~/.ssh/rsync_id_ed25519 + deploy_path: '{{CI_PROJECT_DIR}}/Build/cache/{{ENVIRONMENT}}' + repository: git@gitea.fischer.im:evoWeb/website_evoweb_de.git composer_options: '{{composer_action}} --verbose --prefer-dist --no-progress --no-interaction --no-dev --optimize-autoloader --no-suggest --ignore-platform-reqs' prepare_dirs: - .dep - releases - - shared/fileadmin + - shared/public/fileadmin shared_dirs: - public/fileadmin @@ -209,24 +167,31 @@ config: writable_dirs: - public/typo3temp - var + - var/cache + - var/log rsync: exclude_download: - - 'current' - - 'shared/' - - 'var/*' + - current + - shared/ + - var/* - '-,p public/typo3temp' - remote_path: /srv/{{INSTANCE_ID}}/data/htdocs/staging - app_container_path: /usr/local/apache2/htdocs/staging - application_context: Production/Staging - web_domain: https://staging.evoweb.de/ - htaccess: evoweb:website + remote_path: /srv/{{INSTANCE_ID}}/data/htdocs/{{ENVIRONMENT}} + app_container_path: /usr/local/apache2/htdocs/{{ENVIRONMENT}} hosts: - staging: - remote_user: sebastian + staging.evoweb.de: hostname: '{{CI_HOST}}' - deploy_path: '{{CI_PROJECT_DIR}}/Build/cache/{{ENVIRONMENT_NAME}}' - identity_file: ~/.ssh/rsync_id_ed25519 + labels: + environment: staging + application_context: Production/Staging + htaccess: evoweb:website + + www.evoweb.de: + hostname: '{{CI_HOST}}' + labels: + environment: production + application_context: Production + ``` diff --git a/alias.sh b/alias.sh index 6ab0f0d..cc25c65 100644 --- a/alias.sh +++ b/alias.sh @@ -9,7 +9,7 @@ function composer() { --env SSH_AUTH_SOCK=/ssh-agent \ --env CI_HOST \ --env CI_PROJECT_DIR \ - --env ENVIRONMENT_NAME \ + --env ENVIRONMENT \ --env INSTANCE_ID \ --env ADDITIONAL_CONFIG_FILE \ --env TYPO3_CONTEXT \ diff --git a/deploy.php b/deploy.php index dfdad7d..2421d4d 100644 --- a/deploy.php +++ b/deploy.php @@ -1,4 +1,4 @@