Last active
March 9, 2026 15:59
-
-
Save ntd/325b869127011deae24395e5db3ed1df to your computer and use it in GitHub Desktop.
Using `deployer` (without provisioning) to deploy Silverstripe 6 webapps
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 | |
| /** | |
| * This is the shared deployer code I use in my projects. Put it | |
| * somewhere in your filesystem and then, for each Silverstripe project, | |
| * add a `deploy.yml` similar to this one: | |
| * ```yaml | |
| * import: /path/to/deployer8-silverstripe6.php | |
| * | |
| * config: | |
| * application: 'www.my.site' | |
| * | |
| * hosts: | |
| * staging: | |
| * hostname: myvps | |
| * deploy_path: '/var/www/staging' | |
| * production: | |
| * hostname: myvps | |
| * deploy_path: '/var/www/{{application}}' | |
| * ``` | |
| * `dep provision` is **not** needed here: webserver, PHP and database | |
| * configuration are your responsibility. Just be sure to have a working | |
| * ssh connection to your server and to install composer, cachetool, | |
| * unzip, acl and rsync. | |
| */ | |
| namespace Deployer; | |
| require 'recipe/silverstripe.php'; | |
| // Any setting is overridable in `deploy.yml` | |
| set('bin/php', '/usr/bin/php8.4'); | |
| set('bin/composer', '{{bin/php}} /usr/local/bin/composer'); | |
| set('bin/cachetool', '{{bin/php}} /usr/local/bin/cachetool'); | |
| set('bin/dbdump', <<<EOT | |
| /usr/bin/mysqldump --compact --insert-ignore --add-drop-table \ | |
| -h"\$SS_DATABASE_SERVER" -u"\$SS_DATABASE_USERNAME" \ | |
| -p"\$SS_DATABASE_PASSWORD" "\$SS_DATABASE_NAME" | |
| EOT | |
| ); | |
| set('webroot', './'); | |
| set('dotenv', '.env.{{alias}}'); | |
| set('upload_options', [ | |
| '-R', | |
| // The `-FF` argument allows to override the rules below | |
| // with local `.rsync-filter` files: see `man rsync` | |
| '-FF', | |
| // Default filter rules: work if your whole silverstripe | |
| // application is under `app` or `main` | |
| '--filter=+ /app/***', | |
| '--filter=+ /composer.*', | |
| '--filter=+ /main/***', | |
| '--filter=+ /themes/***', | |
| '--filter=- /public/assets/', | |
| '--filter=- /public/resources/', | |
| '--filter=- /public/_resources/', | |
| '--filter=+ /public/***', | |
| '--filter=- *', | |
| ]); | |
| // The user/group guessing logic in deployer is too fragile | |
| set('http_user', 'www-data'); | |
| set('http_group', 'www-data'); | |
| task('deploy:env', function () { | |
| upload( | |
| get('dotenv'), | |
| '{{release_or_current_path}}/.env', | |
| [ 'progress_bar' => false ] | |
| ); | |
| }); | |
| // Use `rsync` for updating code on the remote host | |
| task('deploy:update_code', function () { | |
| upload( | |
| get('webroot'), | |
| get('release_or_current_path'), | |
| [ 'options' => get('upload_options') ] | |
| ); | |
| }); | |
| // If `deployer` is run by different user from the web user (as I do), | |
| // `sake db:build` will fail when regenerating the static error pages | |
| // if the old ones were generated from the web. | |
| // Avoid the problem by removing them before the regeneration process. | |
| task('silverstripe:remove_static_pages', function () { | |
| run('rm {{release_or_current_path}}/{{ shared_assets }}/error-*.html', nothrow: true); | |
| }) | |
| ->hidden(); | |
| before('silverstripe:build', 'silverstripe:remove_static_pages'); | |
| before('silverstripe:buildflush', 'silverstripe:remove_static_pages'); | |
| desc('Clear opcache cache'); | |
| task('cachetool:clear:opcache', function () { | |
| run('{{bin/cachetool}} opcache:reset -n --fcgi'); | |
| }); | |
| after('deploy:success', 'cachetool:clear:opcache'); | |
| desc('Clear APCU cache'); | |
| task('cachetool:clear:apcu', function () { | |
| run('{{bin/cachetool}} apcu:cache:clear -n --fcgi'); | |
| }); | |
| after('deploy:success', 'cachetool:clear:apcu'); | |
| after('deploy:failed', 'deploy:unlock'); | |
| task('backup:info', function () { | |
| info("backup of <fg=green;options=bold>{{what}}</> from <fg=magenta;options=bold>{{where}}</>"); | |
| }) | |
| ->hidden(); | |
| desc('Create a local dump of the remote database'); | |
| task('backup:database', function () { | |
| // Source .env to get database settings | |
| $dump = run('source {{current_path}}/.env && {{bin/dbdump}}'); | |
| // TODO `$dump` contains the whole database: | |
| // this approach is not really scalable | |
| $db = get('application', 'dump'); | |
| file_put_contents("./$db.sql", $dump); | |
| }); | |
| desc('Fetch assets from the remote'); | |
| task('backup:assets', function () { | |
| download( | |
| '{{deploy_path}}/shared/{{shared_assets}}/', | |
| './{{shared_assets}}/', | |
| [ 'options' => [ '--delete', '-a' ] ] | |
| ); | |
| }); | |
| task('backup:success', function () { | |
| info('backup terminated successfully!'); | |
| }) | |
| ->hidden(); | |
| desc('Backup your project'); | |
| task('backup', [ | |
| 'backup:info', | |
| 'backup:database', | |
| 'backup:assets', | |
| 'backup:success', | |
| ]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment