Last active
December 1, 2021 15:21
-
-
Save koehnlein/8db25464bf9597f8ed962a8bcf6f8a29 to your computer and use it in GitHub Desktop.
simple deployment based on TYPO3 Surf and Gitlab CI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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:[email protected]'; | |
$context = 'Production/Staging'; | |
} | |
// Set this if you do not have a remote repository | |
$repositoryUrl = '[email protected]: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); | |
}); |
Thank you Kay, for your feedback. Sounds interesting. How do you include/read/parse that yaml files? Do you have an example?
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?
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
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 ;)