You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I wanted to share the recipe I have made for my Symfony projects which are hosted on a shared server.
On such server, I can't control neither composer, node or yarn version, and I neither know when they are going to be updated. In such situation, if you need to deploy quickly a fix for example, you can get stuck due to breaking change and impossibility to deploy code unless you upgrade your local version of these software to resolves bugs, which can lead to several hours or days of work to upgrade the libraries installed by these managers (because they become impossible to install with the new manager version) while leaving the prod down or unpatched at least.
So the solution is to download and build the assets locally (on your computer or using your CI/CD), and then upload all the files to the server. It is not a perfect workaround, but it is the most reliable process I have found for that kind of situation.
Here is the config I use to do so, which is agnostic from were you upload your code (locally or via your CI/CD).
./deploy.yaml
import:
- ./deployer/recipe/symfony_build.phpconfig:
application: 'APP_NAME'keep_releases: 5writable_mode: 'chown'# By default, every root directories are going to be uploaded, except the followingexcluded_dirs:
- '.*'
- 'build'
- 'deployer'
- 'docker'
- 'node_modules'
- 'tests'
- 'tools'
- 'var'
- 'vendor'# By default, every root files are NOT going to be uploaded, except the followingincluded_files:
- '.env'
- 'composer.json'
- 'composer.lock'after:
deploy:failed: 'deploy:unlock'
./deployer/recipe/symfony_build.php
<?phpnamespaceDeployer;
useDeployer\Exception\Exception;
require'recipe/symfony.php';
//// Config//// If PR #3420 is merged, this include can be removedrequire__DIR__ . '/../config/symfony_build.php';
set('local_project_dir', __DIR__ . '/../..');
if (get('local_build_dir', '') === '') {
set('local_build_dir', '{{local_project_dir}}/.build');
}
if (get('local_release_dir', '') === '') {
set('local_release_dir', '{{local_project_dir}}/.release');
}
if (get('local_release_file', '') === '') {
set('local_release_file', '{{local_release_dir}}/{{release_name}}');
}
if (get('release_dir', '') === '') {
set('release_dir', '{{deploy_path}}/releases/{{release_name}}');
}
//// Hooks//// Build archive by copying relevant source code into a separate temp directorytask('build', function () {
// Create needed temp directories and remove files from previous deployment if neededif (testLocally('[ -d {{local_build_dir}} ]')) {
runLocally('rm -r "{{local_build_dir}}"');
}
if (testLocally('[ ! -d {{local_build_dir}} ]')) {
runLocally('mkdir "{{local_build_dir}}"');
}
if (testLocally('[ ! -d {{local_release_dir}} ]')) {
runLocally('mkdir "{{local_release_dir}}"');
}
if (testLocally('[ -f {{local_release_file}} ]')) {
runLocally('rm "{{local_release_file}}"');
}
// By default, every root directories are going to be copied, except the following$excludedDirsList = '';
if (get('excluded_dirs', []) !== []) {
$excludedDirsList = \implode("' ! -name '", get('excluded_dirs'));
$excludedDirsList = "! -name '$excludedDirsList'";
}
runLocally("find . -maxdepth 1 -type d $excludedDirsList -exec cp -R '{}' '{{local_build_dir}}/{}' \;");
// By default, every root files are NOT going to be copied, except the followingif (get('included_files', []) !== []) {
$includedFilesList = \implode('', get('included_files'));
runLocally("cp $includedFilesList {{local_build_dir}}/");
} else {
runLocally("find . -maxdepth 1 -type f -exec cp '{}' '{{local_build_dir}}/{}' \;");
}
// Build the archive and delete copied source files from the temp directoryrunLocally('tar -czf {{local_release_file}} .', ['cwd' => get('local_build_dir')]);
runLocally('rm -r "{{local_build_dir}}"');
})->once()->desc('Build project');
// Override recipe/common::deploy:update_codetask('deploy:update_code', function () {
if (parse('release_name') === '') {
thrownewException('release_name is empty');
}
upload('{{local_release_file}}', '{{release_dir}}');
cd('{{release_dir}}');
run('tar --strip-components 1 -xf {{release_name}}');
run('rm {{release_name}}');
run('composer dump-autoload');
})->desc('Upload project files to host');
// Override recipe/symfony::deploy (add jobs: build, database:migrate and doctrine:schema:validate)task('deploy', [
'build',
'deploy:prepare',
'deploy:vendors',
'database:migrate',
'doctrine:schema:validate',
'deploy:cache:clear',
'deploy:publish',
]);
./deployer/config/symfony_build.php (If PR #3420 is merged, this file is useless and the config would be moved into deploy.yaml)
<?phpnamespaceDeployer;
// Override composer_options to allow dev dependencies to be installed in staging environmentset('COMPOSER_OPTIONS', trim(\getenv('COMPOSER_OPTIONS')));
set('release_name', trim(\getenv('ARCHIVE_RELEASE_NAME')));
set('http_user', trim(\getenv('HOST_HTTP_USER')));
host('staging')
->setHostname(trim(\getenv('STAGING_HOST')))
->setRemoteUser(trim(\getenv('STAGING_REMOTE_USER')))
->setDeployPath('~/{{application}}/{{alias}}');
host('preprod')
->setHostname(trim(\getenv('PREPROD_HOST')))
->setRemoteUser(trim(\getenv('PREPROD_REMOTE_USER')))
->setDeployPath('~/{{application}}/{{alias}}');
host('prod')
->setHostname(trim(\getenv('PROD_HOST')))
->setRemoteUser(trim(\getenv('PROD_REMOTE_USER')))
->setDeployPath('~/{{application}}/{{alias}}');
And finally, the part of my CI/CD config, which set the app version number which appears in the footer of my app.
In that way, as the deploy job run only if all the pipeline is OK, I'm sure I'm sending to my server a working package.
Or if I want to deploy from my computer:
./tools/deployer/deploy.sh
#!/bin/bash
CURRENT_DIR="$(cd"$( dirname "${BASH_SOURCE[0]}")"&> /dev/null &&pwd)"if [[ -z$1 ]];thenecho"The name of the remote server to deploy the code to is expected as the first arg."exit 1
fi
REMOTE_NAME="$1"# Clean .env file
ENV_FILE="${CURRENT_DIR}/.env"if [[ -f${ENV_FILE} ]];then
rm "${ENV_FILE}"fi
touch "${ENV_FILE}"# Populate .env file
BASE_ENV_FILE="${CURRENT_DIR}/../../.env.local"if [[ -f${BASE_ENV_FILE} ]];then
cat "${BASE_ENV_FILE}">"${ENV_FILE}"
sed -i "s#{{archive_release_name}}#$(git rev-parse --abbrev-ref HEAD 2> /dev/null)#g""${ENV_FILE}"fi# shellcheck disable=SC2046# shellcheck disable=SC2002# Load env vars from .env file only in the scope of this command line and perform deployment
env $(cat "${ENV_FILE}"| xargs) php vendor/bin/dep deploy "${REMOTE_NAME}" -vvv
This script needs to add to .gitignore /tools/deployer/.env.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
I wanted to share the recipe I have made for my Symfony projects which are hosted on a shared server.
On such server, I can't control neither composer, node or yarn version, and I neither know when they are going to be updated. In such situation, if you need to deploy quickly a fix for example, you can get stuck due to breaking change and impossibility to deploy code unless you upgrade your local version of these software to resolves bugs, which can lead to several hours or days of work to upgrade the libraries installed by these managers (because they become impossible to install with the new manager version) while leaving the prod down or unpatched at least.
So the solution is to download and build the assets locally (on your computer or using your CI/CD), and then upload all the files to the server. It is not a perfect workaround, but it is the most reliable process I have found for that kind of situation.
Here is the config I use to do so, which is agnostic from were you upload your code (locally or via your CI/CD).
./deploy.yaml
./deployer/recipe/symfony_build.php
./deployer/config/symfony_build.php (If PR #3420 is merged, this file is useless and the config would be moved into deploy.yaml)
And finally, the part of my CI/CD config, which set the app version number which appears in the footer of my app.
./gitlab-ci.yaml
In that way, as the deploy job run only if all the pipeline is OK, I'm sure I'm sending to my server a working package.
Or if I want to deploy from my computer:
./tools/deployer/deploy.sh
This script needs to add to .gitignore
/tools/deployer/.env
.Hope this help :)
Beta Was this translation helpful? Give feedback.
All reactions