Magento 2 and deploying has quite some history. Before Magento 2.2 it was not possible to deploy Magento without any downtime. It needed a database connection to determine the deployment version. Luckily, things have changed for the good.
The Deployer Plus version by Juan Alonso was the default for a long time. It included everything that you needed to deploy Magento to your server. The downside of this version is that the development seems slow or dead. The last commit at the time of writing is 29 Dec 2021.
Luckily, another option is the included Magento 2 recipe from Deployer itself. The next steps are based on that.
Getting started
The first thing we need to do is install Deployer (composer require deployer/deployer
) and create the deploy.php
file. This can be done by running vendor/bin/dep init
and selecting Magento 2 as option.
localhost
Ensure you have set the correct settings in your deploy.php: a repository and a host are required. This recipe also requires another host: localhost. This is required because the artifact approach is being used: The build is first done locally, and everything is packed into a file, which is then uploaded and unpacked. For this to work you need to add this line to your deploy.php:
localhost()->set('local', true);
Next to this, you also need to add at least one host to deploy to. You will usually have at least 2 (production and staging) or more. The examples in this article count on two different hosts: production and staging.
host('my-webshop.com') ->set('remote_user', 'deploy') ->set('deploy_path', '/var/www/html/my-webshop.com/') ->setHostname('123.456.789.123') ->setLabels(['stage' => 'production', 'branch' => 'main']); host('staging.my-webshop.com') ->set('remote_user', 'deploy') ->set('deploy_path', '/var/www/html/staging.my-webshop.com/') ->setHostname('123.456.789.123') ->setLabels(['stage' => 'staging', 'branch' => 'develop']);
Theme options
There are also some optional settings for the setup:static-content:deploy
step:
You can set the required languages:
set('static_content_locales', 'en_US en_GB');
The number of jobs:
set('static_content_jobs', '12');
Themes to compile. Not setting this will compile all themes:
set('magento_themes', ['Magento/luma']);
Any other options you want to pass to the compile command (optional):
set('static_deploy_options', '-f');
Hyvä support
Adding support for Hyvä is pretty easy. Just add these lines to your deploy.php
file. I've added them before the deploy:unlock
line:
// Deploy Hyva task('hyva:deploy', function () { run('npm --prefix vendor/hyva-themes/magento2-default-theme/web/tailwind/ ci'); run('npm --prefix vendor/hyva-themes/magento2-default-theme/web/tailwind/ run build'); }); before('magento:deploy:assets', 'hyva:deploy');
Exclude list
We will also need an exclude list file. The files on this list will not be included in the artifact. The artifact itself needs to be in this file, or else we will get an error because the file is altered during the compression. This makes sense, as we can't pack a file into itself.
For this, create a file in artifacts/exclude
with this content:
artifacts/*
Preparing Magento
When trying to run these steps in GitHub Actions, Magento will try to connect to the database to which we don't have access. To prevent this, Magento has the option to dump the configuration settings around the stores and themes in your app/etc/config.php
file. To do that, run this command:
bin/magento app:config:dump scopes themes
Now make sure that you commit these changes before continuing.
Preparing GitHub Actions
GitHub Actions needs access to your server. For this, we need access to a private/public key pair. The private key needs to be in GitHub Actions; the public key needs to be placed on your server in ~/.ssh/authorized_keys
.
Open your repository and navigate to Settings -> Secrets and Variables -> Actions. Click "Add new repository secret".
Name:
SSH_PRIVATE_KEY
Value:
-- Insert your private key --
A key pair can be generated locally, via a website, or, my preference, through 1Password.
deploy.yml
This is the deploy.yml I'm using. It is placed in .github/workflows/deploy.yml
:
name: Deploy on: push: branches: - main - develop jobs: deploy: concurrency: deploy-${{ github.head_ref || github.ref_name }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Cache vendor uses: actions/cache@v3 with: path: | vendor !vendor/magento/magento2-base/ ~/.composer/cache key: vendor-${{ hashFiles('**/composer.lock') }} - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: '8.1' - name: Setup Node uses: actions/setup-node@v2 with: node-version: '16' - name: Install Composer dependencies run: composer install --no-dev --prefer-dist --optimize-autoloader - name: Build artifact run: vendor/bin/dep artifact:build localhost - name: Deploy artifact uses: deployphp/action@v1 with: private-key: ${{ secrets.SSH_PRIVATE_KEY }} dep: artifact:deploy branch=${{ github.head_ref || github.ref_name }} - name: Upload artifact uses: actions/upload-artifact@v3 if: always() with: name: Artifact - ${{ github.head_ref || github.ref_name }} path: | artifacts/artifact.tar.gz
Let's go through it.
On push
This code:
on: push: branches: - main - develop
Makes sure that Magento is only build & deployed when a push is done on the main and develop branches. Any other branches will be ignored.
Concurrency
jobs: deploy: concurrency: deploy-${{ github.head_ref || github.ref_name }}
This line makes sure that there is only 1 deployment per branch. This makes sure you don't get conflicts where the first push locks the deployment and the next push fails because the deployment is locked.
Steps
uses: actions/checkout@v2
This makes sure the code is pulled from GitHub into the runner.
name: Cache vendor
Composer is pretty quick nowadays, but it can be even quicker by caching the files. The magento/magento2-base
is not being cached. The reason for this is that a composer install
action will trigger some scripts that copy bin/magento
and other required files.
name: Setup PHP
We need PHP, so make sure it is available.
name: Setup Node
This is mainly for Hyvä, but make sure that Node and NPM are available.
name: Install Composer dependencies
This speaks for itself.
name: Build artifact
This is where the first magic happens: All steps that can be done locally will be done. setup:di:compile
, setup:static-content:deploy
, etc. The last action in this step will pack everything into a file for the next step.
name: Deploy artifact
The file generated in the previous step will be uploaded and unpacked. Also, some of the steps that can only be done on the server will be executed here. Things like creating symlinks to app/etc/env.php
and running setup:upgrade
if required.
name: Upload artifact
This step is optional: The generated artifact file will be uploaded and available withing GitHub actions when the build is complete. This way, you can always inspect the generated code afterward.
Hey, I'm running a developer focussed newsletter called MageDispatch.com, which is for the community and by the community. Here you can share links that you think that the community should know about. If you like this kind of technical content you will like this newsletter too.
Want to respond?