Configure Deployment via GitLab
To automatically deploy via gitlab we need to configure
{project_name}
| .gitlab-ci.yml
└─── deploy
| deploy.php
└─── recipes
| rsync.php
./.gitlab-ci.yml
image: php:7.4-apache
before_script:
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
- ssh-keyscan -H staging.ext.dev >> ~/.ssh/known_hosts
- apt-get update -yqq
- apt-get install -yqq git libmcrypt-dev libpq-dev libcurl4-gnutls-dev libicu-dev libvpx-dev libjpeg-dev libpng-dev libxpm-dev zlib1g-dev libfreetype6-dev libxml2-dev libexpat1-dev libbz2-dev libgmp3-dev libldap2-dev unixodbc-dev libsqlite3-dev libaspell-dev libsnmp-dev libpcre3-dev libtidy-dev rsync zip libzip-dev
- docker-php-ext-install zip
- docker-php-ext-install gd
- curl -sS getcomposer.org/installer | php
- php composer.phar self-update 1.10.16
- php composer.phar install
- curl -LO deployer.org/deployer.phar
- chmod +x deployer.phar
deploy_staging:
only:
- staging
script:
- php deployer.phar --file=deploy/deploy.php deploy staging
./deploy/deploy.php
Make sure to replace [domain] with the actual domain of the TYPO3 website.
<?php
namespace Deployer;
require 'recipe/common.php';
require 'recipes/rsync.php';
set('keep_releases', 5);
set('rsync', [
'exclude' => [
'public/fileadmin',
'public/typo3temp',
'docker',
'deploy',
'.git',
'.gitignore',
'.gitlab-ci.yml',
'docker-compose.yml'
],
'exclude-file' => false,
'include' => [],
'include-file' => false,
'filter' => [],
'filter-file' => false,
'filter-perdir' => false,
'flags' => 'rz',
'options' => [
'delete',
'links'
],
'timeout' => 3600,
]);
set('rsync_src', 'src/typo3');
set('bin/php', 'php74.bin.cli -d allow_url_fopen=On');
set('shared_files', [
'public/typo3conf/AdditionalConfiguration.php'
]);
set('shared_dirs', [
'public/fileadmin',
'public/typo3temp',
'public/uploads',
'var'
]);
set('writable_dirs', [
'public/fileadmin',
'public/typo3temp',
'public/uploads',
'var'
]);
host('staging.ext.dev')
->stage('staging')
->user('root')
->set('http_user', 'www-data')
->set('writable_mode', 'chown')
->set('rsync_src', '.')
->set('rsync_dest','{{release_path}}')
->set('deploy_path', '/var/www/deployments/[domain].staging.ext.dev');
// Tasks...
task('deploy', [
'deploy:prepare',
'deploy:lock', // lock, prevent synchrone deployments in same project
'deploy:release',
'rsync:warmup', // rsync reuse previous release
'rsync', // rsync new code
'deploy:shared',
'deploy:writable',
'deploy:clear_paths',
'deploy:symlink',
'deploy:unlock',
'cleanup',
'success'
]);
// [Optional] if deploy fails automatically unlock.
after('deploy:failed', 'deploy:unlock');
./deploy/recipes/rsync.php
This file is identical for all project - the code can simply be copy/pasted.
<?php
/* (c) HAKGER[hakger.pl] Hubert Kowalski <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @contributor Steve Mueller <[email protected]>, Niklas Vosskoetter <[email protected]>
*/
namespace Deployer;
set('rsync', [
'exclude' => [
'.git',
'deploy.php',
],
'exclude-file' => false,
'include' => [],
'include-file' => false,
'filter' => [],
'filter-file' => false,
'filter-perdir' => false,
'flags' => 'rz',
'options' => ['delete'],
'timeout' => 300,
]);
set('rsync_src', __DIR__);
set('rsync_dest', '{{release_path}}');
set('rsync_excludes', function () {
$config = get('rsync');
$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;
});
set('rsync_includes', function () {
$config = get('rsync');
$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;
});
set('rsync_filter', function () {
$config = get('rsync');
$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 $filterFile'";
}
return $filtersRsync;
});
set('rsync_options', function () {
$config = get('rsync');
$options = $config['options'];
$optionsRsync = [];
foreach ($options as $option) {
$optionsRsync[] = "--$option";
}
return implode(' ', $optionsRsync);
});
desc('Warmup remote Rsync target');
task('rsync:warmup', function() {
$config = get('rsync');
$source = "{{deploy_path}}/current";
$destination = "{{deploy_path}}/release";
if (test("[ -d $(echo $source) ]")) {
run("rsync -{$config['flags']} {{rsync_options}}{{rsync_excludes}}{{rsync_includes}}{{rsync_filter}} $source/ $destination/");
} else {
writeln("<comment>No way to warmup rsync.</comment>");
}
});
desc('Rsync local->remote');
task('rsync', function() {
$config = get('rsync');
$src = get('rsync_src');
while (is_callable($src)) {
$src = $src();
}
if (!trim($src)) {
// if $src is not set here rsync is going to do a directory listing
// exiting with code 0, since only doing a directory listing clearly
// is not what we want to achieve we need to throw an exception
throw new \RuntimeException('You need to specify a source path.');
}
$dst = get('rsync_dest');
while (is_callable($dst)) {
$dst = $dst();
}
if (!trim($dst)) {
// if $dst is not set here we are going to sync to root
// and even worse - depending on rsync flags and permission -
// might end up deleting everything we have write permission to
throw new \RuntimeException('You need to specify a destination path.');
}
$server = \Deployer\Task\Context::get()->getHost();
if ($server instanceof \Deployer\Host\Localhost) {
runLocally("rsync -{$config['flags']} {{rsync_options}}{{rsync_excludes}}{{rsync_includes}}{{rsync_filter}} '$src/' '$dst/'", $config);
return;
}
$host = $server->getRealHostname();
$port = $server->getPort() ? ' -p' . $server->getPort() : '';
$sshArguments = $server->getSshArguments();
$user = !$server->getUser() ? '' : $server->getUser() . '@';
runLocally("rsync -{$config['flags']} -e 'ssh$port $sshArguments' {{rsync_options}}{{rsync_excludes}}{{rsync_includes}}{{rsync_filter}} '$src/' '$user$host:$dst/'", $config);
});
Configure config.yaml
Finally Make sure the file /config/sites/{sitename}/config.yaml file of the TYPO3 project has a basevariant for the staging.ext.dev hostname environment:
base: 'https://{sitename}/'
baseVariants:
-
base: 'http://localhost/'
condition: 'getenv("HOSTNAME") == "localhost"'
-
base: 'https://{sitename}.staging.ext.dev/'
condition: 'getenv("HOSTNAME") == "{sitename}.staging.ext.dev"'