I recently held a live stream where I walked through the continuous integration and deployment (CI/CD) of a Drupal project to DigitalOcean’s App Platform and other CI/CD items. App Platform has its quirks, but it’s simple to build an application with various components. My project, Whiskey Dex, builds a Docker image that pushes to my container registry and then updates my App Platform manifest to use the new image tag, triggering a deployment.
One thing it, and other similar platforms, lacks is the ability to perform operations once a deployment is finished. After a deployment, that is when you need to run schema migrations and other automated updates. It also is missing the ability to add a Cron component to run particular tasks on a schedule. Does provide Worker components, however. A worker is a service that isn’t exposed over HTTP and will restart if its script exits or errors.
Previously I wrote about using ReactPHP to run Drush tasks at controlled intervals in a worker script. This approach was used to execute various tasks at different intervals when cron granularity was at a minimum of 5 minutes and also needed extra failure handling and reporting. What I didn’t think about until recently was using the boot of my worker to handle deployment tasks.
My solution for Whiskey Dex deployments on App Platform to perform post-deployment actions and cron was to leverage a Worker component that executes a ReactPHP script. The main application serves Drupal over HTTP. The worker would run the script to perform my other required tasks in the background.
Worker script for deployments and cron
My deployment script is a really simple PHP script:
- When executed, run database updates and configuration import
- Every ten minutes, run Drupal’s cron
This could be a pretty simple
while(true) loop, where I manage the event loop myself and timing when cron was last executed. But I have plans for this to be scaled out and have concurrent operations for processing other queues. ReactPHP provides my event loop, intervals, and timers. This allows me to write event-driven and non-blocking code easily instead of managing it myself. I don't need to manage the
while loop or track timing. Using the ChildProcess component from ReactPHP, I can include async processing with commands triggered at any point of the loop.