Skip to content

Instantly share code, notes, and snippets.

@koehnlein
Last active December 1, 2021 15:21
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save koehnlein/8db25464bf9597f8ed962a8bcf6f8a29 to your computer and use it in GitHub Desktop.
Save koehnlein/8db25464bf9597f8ed962a8bcf6f8a29 to your computer and use it in GitHub Desktop.
simple deployment based on TYPO3 Surf and Gitlab CI
stages:
- deploy
# use caching
cache:
key: "$CI_BUILD_REF_NAME/$CI_BUILD_STAGE"
paths:
- .caches/
variables:
COMPOSER_CACHE_DIR: "$CI_PROJECT_DIR/.caches/composer"
SURF_WORKSPACE: "$CI_PROJECT_DIR/.caches/surf_workspace"
image: t3easy/surf:node
before_script:
#
# SSH key for connection to remote server
# @see https://docs.gitlab.com/ee/ci/ssh_keys/README.html
#
# Install ssh-agent if not already installed, it is required by Docker.
# (change apt-get to yum if you use a CentOS-based image)
- 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
# Run ssh-agent (inside the build environment)
- eval $(ssh-agent -s)
# Add the SSH key stored in SSH_PRIVATE_KEY variable to the agent store
- ssh-add <(echo "$SSH_PRIVATE_KEY")
# For Docker builds disable host key checking. Be aware that by adding that
# you are suspectible to man-in-the-middle attacks.
# WARNING: Use this only with the Docker executor, if you use it with shell
# you will overwrite your user's SSH config.
- mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
# In order to properly check the server's host key, assuming you created the
# SSH_SERVER_HOSTKEYS variable previously, uncomment the following two lines
# instead.
# - mkdir -p ~/.ssh
# - '[[ -f /.dockerenv ]] && echo "$SSH_SERVER_HOSTKEYS" > ~/.ssh/known_hosts'
# set npm cache to a folder that can be cached from gitlab ci
- npm config set cache $CI_PROJECT_DIR/.caches/npm
staging:
stage: deploy
script:
- surf deploy
only:
- master
live:
stage: deploy
when: manual
script:
- surf deploy
only:
- master
variables:
SURF_CONTEXT: 'Production'
<?php
/** @var \TYPO3\Surf\Domain\Model\Deployment $deployment */
use TYPO3\Surf\Domain\Model\Node;
use TYPO3\Surf\Domain\Model\SimpleWorkflow;
$projectName = 'My TYPO3 Project';
switch(getenv('SURF_CONTEXT')) {
case 'Production':
echo "Not defined yet!\n";
die(1);
$deploymentPath = '/srv/www/vhosts.d/myproject-on-live-server';
$deploymentHost = 'www.example.com';
$deploymentUser = 'myproject';
$repositoryBranch = 'master';
$baseUrl = 'https://www.example.com';
$context = 'Production';
break;
// Staging
default:
$deploymentPath = '/srv/www/vhosts.d/myproject-on-staging-server';
$deploymentHost = 'staging.example.com';
$deploymentPort = 4242;
$deploymentUser = 'root';
$repositoryBranch = 'master';
$baseUrl = 'http://user:pass@staging.example.com';
$context = 'Production/Staging';
}
// Set this if you do not have a remote repository
$repositoryUrl = 'git@gitlab.com:group/myproject.git';
// Set this if your composer command is not available in PATH
//$composerCommandPath = 'composer';
// Set this, if on remote host the correct PHP binary is not available in PATH
//$deployment->setOption('phpBinaryPathAndFilename', '/usr/local/bin/php7-70STABLE-CLI');
// No changes are required in the default case below this point.
$application = new \TYPO3\Surf\Application\TYPO3\CMS();
$deployment->addApplication($application);
// Set default config values
if (!isset($repositoryUrl)) {
$repositoryUrl = 'file://' . dirname(__DIR__);
}
if (!isset($repositoryBranch)) {
$repositoryBranch = getenv('DEPLOY_BRANCH') ?: 'master';
}
if (!isset($composerCommandPath)) {
$composerCommandPath = 'composer';
}
$node = new Node($deploymentHost);
$node->setHostname($deploymentHost);
if(isset($deploymentUser)) {
$node->setOption('username', $deploymentUser);
}
if(isset($deploymentPort)) {
$node->setOption('port', $deploymentPort);
}
$application->addNode($node);
$application->setOption('projectName', $projectName);
$application->setOption('repositoryUrl', $repositoryUrl);
$application->setOption('branch', $repositoryBranch);
$application->setOption('webDirectory', 'htdocs');
$application->setDeploymentPath($deploymentPath);
$application->setOption('keepReleases', 5);
$application->setOption('composerCommandPath', $composerCommandPath);
if($context) {
$application->setContext($context);
} else {
$application->setContext('Production');
}
// clear opcache
$application->setOption('baseUrl', $baseUrl);
$application->setOption(
'rsyncExcludes',
[
'.DS_Store',
'Thumbs.db',
'/composer.json',
'/composer.lock',
'/.data',
'/docker-*',
'/.editorconfig',
'/.git*',
'/Gulpfile.js',
'/package.json',
'/node_modules',
'/README*',
'/.surf',
'/htdocs/fileadmin',
'/htdocs/uploads',
]
);
// Make sure we build from a clean state
$application->setOption('TYPO3\\Surf\\Task\\Package\\GitTask[hardClean]', true);
$deployment->onInitialize(function() use ($deployment, $application) {
/** @var SimpleWorkflow $workflow */
$workflow = $deployment->getWorkflow();
// build files
$workflow->defineTask('Koehnlein\\Distribution\\DefinedTask\\BuildTask', 'TYPO3\\Surf\\Task\\LocalShellTask', array(
'command' => array(
"cd {workspacePath}",
"npm install",
"npm run-script build"
),
));
$workflow->afterStage('package', 'Koehnlein\\Distribution\\DefinedTask\\BuildTask');
/*
// index.php as symlink not working on some servers (e.g. jweiland.net)
$workflow->defineTask('Koehnlein\\Distribution\\DefinedTask\\CopyIndexPhp', 'TYPO3\\Surf\\Task\\ShellTask', array(
'command' => array(
"rm {releasePath}/htdocs/index.php",
"cp {releasePath}/vendor/typo3/cms/index.php {releasePath}/htdocs/index.php",
)
));
$workflow->afterStage('transfer', 'Koehnlein\\Distribution\\DefinedTask\\CopyIndexPhp');
*/
// configuration files
$workflow->defineTask('Koehnlein\\Distribution\\DefinedTask\\SymlinkLocalConfigurationTask', 'TYPO3\\Surf\\Task\\Generic\\CreateSymlinksTask', [
'symlinks' => [
'htdocs/.htaccess' => '../../../shared/Data/.htaccess'
'htdocs/typo3conf/LocalConfiguration.php' => '../../../../shared/Data/typo3conf/LocalConfiguration.php',
]
]);
$workflow->addTask('Koehnlein\\Distribution\\DefinedTask\\SymlinkLocalConfigurationTask', 'update', $application);
// clear opcache, see https://stackoverflow.com/a/40760221/7358195
$application->setOption(
'scriptBasePath',
$deployment->getWorkspacePath($application) . '/' . $application->getOption('webDirectory')
);
$workflow->addTask('TYPO3\\Surf\\Task\\Php\\WebOpcacheResetCreateScriptTask', 'package', $application);
$workflow->addTask('TYPO3\\Surf\\Task\\Php\\WebOpcacheResetExecuteTask', 'switch', $application);
});
@kaystrobach
Copy link

i try to avoid as many hard coded values by moving that stuff into env vars, which are then defined either in the gitlab group / project or yaml file, this makes this kind of scripts even smaller and more generic ;)

@koehnlein
Copy link
Author

Thank you Kay, for your feedback. Sounds interesting. How do you include/read/parse that yaml files? Do you have an example?

@X-Tender
Copy link

I'm new to the whole Gitlab/Surf deployment world and I started to learn it based on you script here but I struggle on some (maybe super basic) points.
First Issue I had that the cache:key variabel was Deprecated which I was able to update but now I struggle with the - ssh-add I know where I have to set the variable but I don't know what I have to place there. Is it the ssh password for the target server where surf is going to deploy it?

@X-Tender
Copy link

OK I've figured it out and it works like a charm! Thanks a lot for this kickstart.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment