Deployments
You can use RockMigrations to create fully automated CI/CD pipelines for Github/Gitlab.
The idea is to replace a workflow that depends on several manual steps with an automated workflow where all you have to do manually is git push
.
Folder Structure
The resulting folder structure of a deployment will look like this (where xxx stands for a release hash from github):
current -> release-xxx // symlink to latest release
release-xxx--- // oldest release
release-xxx--
release-xxx-
release-xxx // latest release
shared // shared folder
The shared
folder will contain the persistent data shared across all releases (like site/assets/files
and site/config-local.php
).
Workflow Log
RockMigrations will create a nice and helpful log on every run:
Setup Variables
This section will list the most important settings of your deployment:
Variables Log
BRANCH: main
PATH: /path/to/your/folder
SSH_USER: youruser
SSH_HOST: yourhost.com
SUBMODULES: true
Deploy via RSYNC
This section will list all files that have been copied from the checked out release to your server.
Trigger RockMigrations Deployment
This section lists the log that is produced by the RockMigrations Deployment from the \RockMigrations\Deployment
class which is triggered by the invokation of /site/deploy.php
after all files have been copied via rsync.
The log is quite long and verbose so everything should be clear from reading that log.
Setup
Add the /site/deploy.php file
The first thing we need to do is to create the PHP file that is triggered at the end of the rsync:
/site/deploy.php
<?php
namespace RockMigrations;
require_once __DIR__ . "/modules/RockMigrations/classes/Deployment.php";
$deploy = new Deployment($argv ?? []);
// custom settings go here
// see docs about "Customising the Deployment"
$deploy->run();
For the first deployment you can copy and paste this file as it is!
Setup SSH Keys
Github needs to be able to copy files to your remote server. That's why we need to setup SSH keys that we store in the Github Repo's secrets.
ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rockmigrations -C "rockmigrations-deployment"
If you are using RockMigrations on multiple projects you can simply overwrite the key on the next deployment setup as you will only need it once during setup. You can also remove that key from your system after you have sucessfully setup your deployment.
Add Secrets to your Repo
Copy the content of the private key to your git secret SSH_KEY
:
cat ~/.ssh/id_rockmigrations
Copy the content of keyscan to your git secret KNOWN_HOSTS
ssh-keyscan your.server.com
Add the public key to your remote user (replace user
and your.server.com
with your custom values):
ssh-copy-id -i ~/.ssh/id_rockmigrations user@your.server.com
Or copy and paste the content of the public key into the ~/.ssh/authorized_keys
file of your remote server. To get the content of the public key you can use this command:
cat ~/.ssh/id_rockmigrations.pub
Try to ssh into your server without using a password:
ssh -i ~/.ssh/id_rockmigrations user@your.server.com
Optional: Create the test-ssh workflow
If you are new to RockMigrations Deployment I recommend an additional step to check if the SSH connection between Github and your server works. This workflow will not copy any files and will therefore be a lot faster. This makes debugging easier.
.github/workflows/deploy.yaml
name: Deploy via RockMigrations
# run this test-workflow on every push to every branch
on:
push
jobs:
test-ssh:
uses: baumrock/RockMigrations/.github/workflows/test-ssh.yaml@main
with:
SSH_USER: youruser
SSH_HOST: your.server.com
secrets:
SSH_KEY: ${{ secrets.SSH_KEY }}
KNOWN_HOSTS: ${{ secrets.KNOWN_HOSTS }}
Commit the change and push to your repo. You should see the workflow showing up in Github's Actions tab:
Create the final workflow file
Once you got your SSH connection up and running you can setup the deployment.
I always create a workflow file for every branch: main.yaml
that fires on pushes to the main
branch and dev.yaml
that fires when I push to dev
:
/.github/workflows/main.yaml
name: Deploy via RockMigrations
on:
push:
branches:
- main
jobs:
deploy-to-production:
uses: baumrock/RockMigrations/.github/workflows/deploy.yaml@main
with:
PATH: "/path/to/www.yoursite.com"
SSH_USER: youruser
SSH_HOST: your.server.com
secrets:
SSH_KEY: ${{ secrets.SSH_KEY }}
KNOWN_HOSTS: ${{ secrets.KNOWN_HOSTS }}
Optional: Using Submodules
If you are using submodules just set the SUBMODULES
input variable to true
and add a CI_TOKEN
to your repo secrets:
/.github/workflows/deploy.yaml
name: Deploy via RockMigrations
on:
push:
branches:
- main
jobs:
deploy-to-production:
uses: baumrock/RockMigrations/.github/workflows/deploy.yaml@main
with:
PATH: "/path/to/www.yoursite.com"
SSH_USER: youruser
SSH_HOST: your.server.com
SUBMODULES: true
secrets:
CI_TOKEN: ${{ secrets.CI_TOKEN }}
SSH_KEY: ${{ secrets.SSH_KEY }}
KNOWN_HOSTS: ${{ secrets.KNOWN_HOSTS }}
See here how to setup a Personal Access Token for Github. You need to create this token only once for your Github Account, not for every project. But you need to add it to every project that should be able to access your private submodules!
First Deployment and Cleanup
If everything worked well you should see a success icon in your Github's Actions
tab.
You can now remove the SSH keypair id_rockmigrations
and id_rockmigrations.pub
from your local system if you want. The private key is stored in your Github's Repository Secrets and the public key is stored on the remote server that accepts connections from your Github action.
Setting up the shared folder
RockMigrations will create the shared folder for you on the first deployment, but it will be empty. To make it useful you need to add at least these two things:
- Add all necessary config settings in
/site/config-local.php
- Upload all files to
/site/assets/files
You only need to do this during setup. Once setup it will just work for all following deployments!
Customising the Deployment
share()
The share method tells RockMigrations that the given file or folder should be symlinked from the shared folder. A good example is the /site/assets/files
folder that is not part of the Github repository but needs to exist in every release.
share()
tells RockMigrations that it should create a symlink to that folder in the shared
directory. You need to upload/create that folder or file yourself. Another example is the file /site/config-local.php
which is also a shared file used by all releases but never touched on deploy.
delete()
You can tell RockMigrations to delete files or folders after deployment. By default it will remove several folders:
/.ddev
/.git
/.github
/site/assets/cache
/site/assets/ProCache
/site/assets/pwpc-*
/site/assets/sessions
As you can see every deployment will wipe the cache and ProCache folder which will make sure that you don't serve outdated versions of your site!
hooks
You can hook into several places of your deployment. See Deployment.php method run()
for all available stages!
This example shows how you can use different .htaccess
files for different environments:
/site/deploy.php
<?php
namespace RockMigrations;
require_once __DIR__ . "/modules/RockMigrations/classes/Deployment.php";
$deploy = new Deployment($argv);
$deploy->after("share", function ($deploy) {
$release = $deploy->paths->release;
$deploy->exec("rm $release/.htaccess");
$deploy->exec("mv $release/.htaccess-staging $release/.htaccess");
});
$deploy->run();
Debugging
Debugging can be hard when using CI/CD pipelines. If you get unexpected results during the PHP deployment you can make the script more verbose like this:
/site/deploy.php
...
$deploy->verbose();
$deploy->run();
You can also make it run in dry mode where no files will be copied and only the list of to be executed commands will be shown:
...
$deploy->dry();
$deploy->run();
Or you can dump basic information with TracyDebugger. For example let's say you don't want to delete the sessions folder on every deploy. You can do so like this:
// site/deploy.php
<?php
namespace RockMigrations;
use function ProcessWire\rockmigrations;
require_once __DIR__ . "/modules/RockMigrations/classes/Deployment.php";
$deploy = new Deployment(@$argv);
$deploy->nodelete("/site/assets/sessions");
if (rockmigrations()->isCLI()) $deploy->run();
else bd($deploy);
And then you can place this in /site/ready.php
:
if (rockmigrations()->isDDEV()) include "deploy.php";
Which will show debugging info like this:
Create Translations
You can either do that manually or by using RockMigrations:
// install german language pack for the default language
// this will install language support, download the ZIP and install it
$rm->setLanguageTranslations('DE');
In our example that created the language with id 1025
(we will use this id for all following examples.
Then make sure that the content of this folder is added to your GIT repo:
# .gitignore
# exclude all files
/site/assets/files/*
# dont ignore files of given page (eg language files)
!/site/assets/files/1025
Then add those files to your repo and commit - this should look something like this:
Now we just need to push this folder to the shared folder on deployment:
/site/deploy.php
// push german translations to staging/production
$deploy->push('/site/assets/files/1025');
Integrations
VSCode has a "github actions" extension that can help you create workflows or inspect workflow runs:
On this page
- Folder Structure
- Workflow Log
- Setup Variables
- Deploy via RSYNC
- Trigger RockMigrations Deployment
- Setup
- Add the /site/deploy.php file
- Setup SSH Keys
- Add Secrets to your Repo
- Optional: Create the test-ssh workflow
- Create the final workflow file
- Optional: Using Submodules
- First Deployment and Cleanup
- Setting up the shared folder
- Customising the Deployment
- share()
- delete()
- hooks
- Debugging
- Create Translations
- Integrations