Skip to content
Latchkey

CI/CD for Drupal with GitHub Actions

Lint, test against MySQL, and deploy your Composer-managed Drupal site.

A modern Drupal site is Composer-managed, with code standards checked by PHPCS and tests run against a database. This recipe installs dependencies, lints, runs tests against MySQL, and prepares a config-import deploy.

What the pipeline does

  • start a MySQL service container
  • install PHP with setup-php
  • install deps with composer install
  • lint with PHPCS
  • run PHPUnit against MySQL

The workflow

The MySQL service container backs the test run via SIMPLETEST_DB. PHPCS uses the Drupal and DrupalPractice standards from coder.

.github/workflows/ci.yml
name: CI
on:
  push:
    branches: [main]
  pull_request:
jobs:
  test:
    runs-on: ubuntu-latest
    services:
      mysql:
        image: mysql:8
        env:
          MYSQL_ROOT_PASSWORD: root
          MYSQL_DATABASE: drupal
        ports: ['3306:3306']
        options: >-
          --health-cmd "mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 5
    env:
      SIMPLETEST_DB: mysql://root:root@127.0.0.1:3306/drupal
    steps:
      - uses: actions/checkout@v4
      - uses: shivammathur/setup-php@v2
        with:
          php-version: '8.3'
          extensions: pdo_mysql, gd
      - run: composer install --no-interaction --prefer-dist
      - run: vendor/bin/phpcs --standard=Drupal,DrupalPractice web/modules/custom
      - run: vendor/bin/phpunit -c web/core web/modules/custom

Caching and speed

Cache the Composer cache directory keyed on composer.lock so installs are quick. Bootstrapping Drupal for kernel and functional tests is the slow part; cheaper managed runners such as Latchkey (around 69% cheaper than GitHub-hosted) keep the suite fast and auto-retry transient MySQL-container startups.

Deploying

Deploy by syncing code to the host, then running composer install --no-dev, drush updatedb, drush config:import, and drush cache:rebuild. Containerized deploys build a php-fpm image and run the same drush steps as an entrypoint or init job.

Key takeaways

  • Test against a MySQL service container via SIMPLETEST_DB.
  • Lint custom modules with the Drupal PHPCS standards.
  • Deploy with drush updatedb and config:import.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →