Search by

syp / drun

stefanpetrov

Robo-based task runner for Drupal 11 sites on Pristan: install, dump, deploy steps, QA and git hooks.

2.1 2026-10-25 16:00 UTC

This package is auto-updated.

Last update: 2026-10-25 16:00:24 UTC


README

A Robo-based task runner for Drupal 11 sites on Pristan. It mirrors what the platform does with a site: its settings.php, its deploy steps, the clean install and the sanitised dump. It also runs commands on the host or in a container, QA tooling and git hooks.

Install

composer require drush/drush       # Pristan builds with --no-dev: keep Drush in require
composer require --dev syp/drun
vendor/bin/run drun:init            # runner.yml, phpcs.xml.dist, phpstan.neon.dist, settings.php and its directories
vendor/bin/run git:hooks-install
echo runner.local.yml >> .gitignore

The project must allow these Composer plugins:

"allow-plugins": {
  "dealerdirect/phpcodesniffer-composer-installer": true,
  "phpstan/extension-installer": true
}

Run vendor/bin/run from the project root.

Commands

CommandDoes
site:install-cleandrush site:install --existing-config, then the deploy steps (Pristan's staging:clean)
site:install-dump [--download] [--no-update]Drop the DB, import drun.dump.path (.sql / .sql.gz), run the deploy steps (Pristan's staging:clone). Downloads the sanitised dump when it is missing or with --download
site:updateRun deploy.site.sh, else drush deploy
qa:lint / qa:phpcs / qa:phpstanRun the tool on existing drun.qa.paths
qa:auditcomposer audit --locked
qa:allRun every QA check and report all failures
git:hooks-install [--force]Write hooks from drun.git.hooks
drun:init [--force] [--docker]Copy the config templates to the project root, write web/sites/default/settings.php and create its directories (settings)

Global options come from Robo: --simulate prints the commands without running them, and -D key=value overrides a config value for one run.

Configuration

vendor/syp/drun/config/default.yml documents every key. It is merged with runner.yml (committed) and then runner.local.yml (not committed).

  • Maps merge recursively. Lists replace the default list.
  • ${dotted.key} tokens are expanded, e.g. ${drun.drush}.

Exec prefix

drun.exec.prefix is prepended to commands that need PHP, Drupal or the database. Git, curl and reading the dump always run on the host.

  • The runner runs where the app does (both on the host, or both inside the container): leave it empty. This is the default.
  • The runner runs on the host and the app runs in Docker: set prefix: 'docker compose exec -T web'. -T is required for dump imports. drun:init --docker writes it to a new runner.yml. Set it in runner.yml for the whole team or in runner.local.yml for one machine; -D drun.exec.prefix= clears it for one run.
  • Inside the container, run docker compose exec web vendor/bin/run ....

The prefix must pass the command's arguments on unchanged, as docker compose exec and docker exec do. One that joins them into a single shell string, such as ssh host, breaks the deploy steps' sh -c.

Deploy steps

site:update, and the end of site:install-dump and site:install-clean, run what Pristan runs on a deploy:

  • deploy.site.sh at the project root, when the file exists. It must be executable (git update-index --chmod=+x deploy.site.sh), else the command fails before it touches anything. It runs with vendor/bin on the PATH and SITE_ENV set, local by default. It replaces drush deploy entirely. Pristan's examples/deploy.site.sh.example is a starting point.
  • drush deploy --yes, when there is no such file.

These steps and the drush site:install of site:install-clean get DRUSH_OPTIONS_URI as on Pristan: https:// and the first of SITE_DOMAINS, unless it is set already. Locally, set it yourself (e.g. http://localhost:8080) when SITE_DOMAINS is set.

Settings

drun:init writes web/sites/default/settings.php, a copy of Pristan's at the time of drun 2.0. Commit it. It reads the environment as Pristan's does, with a random fallback salt for local use, and includes the two files below. Pristan replaces it with its own in the image, so nothing added to it reaches production.

FileOwnerHolds
web/sites/default/settings.phpdrunPristan's settings. Replaced on Pristan. Rewritten by drun:init --force.
settings.site.php (project root)projectThe site's own settings, loaded by Pristan too.
web/sites/default/settings.local.phpmachineOptional, git-ignored, loaded last. Pristan's build refuses the site if it is committed.

The build also refuses any other settings*.php under web/sites. The variables must reach the web container as well as the CLI: in Docker, set them on the web service (see the compose file in Pristan's handbook).

drun:init also creates the directories settings.php points to, when they are missing: config/sync, private and web/sites/default/files, mode 0775. The last two are git-ignored, so run drun:init again after a clone; it keeps the files that exist. Apache in the container writes to them as www-data, which is not you on the host. On the web service:

  • Rootless Docker (docker info lists rootless under Security Options): APACHE_RUN_GROUP: root in environment. The container's root is you on the host, so its group is yours. Do not set user:: any other uid is a stranger on the host.
  • Rootful Docker: user: "1000:1000", your id -u and id -g. Apache and docker compose exec then run as you; without it, exec runs as root and leaves files you cannot change.

Drupal creates its files 0664 and its directories 0775, so either way you can edit and delete them from the host.

Environment

The variables drun and Pristan's settings.php use. On Pristan the app container has them (its handbook's appendix A), except DRUSH_OPTIONS_URI, set for the deploy steps and cron, and GITLAB_*, which only drun reads. Locally, set the ones the site uses on the web service.

VariableRead by
DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORDsettings.php
DRUPAL_HASH_SALTsettings.php
SITE_ENVsettings.php (error level), deploy.site.sh: prod, acc, staging or local
SITE_DOMAINSsettings.php (trusted hosts), comma-separated; the first gives DRUSH_OPTIONS_URI
SITE_REVISIONsettings.php (deployment identifier); the image sets it
MEMCACHE_HOST, MEMCACHE_PORTsettings.php, with the memcache module
SOLR_HOST, SOLR_PORT, SOLR_COREsettings.site.php (search_api_solr)
VARNISH_HOST, VARNISH_PORTsettings.site.php (the purger)
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORDthe image's sendmail
DRUSH_OPTIONS_URIDrush (--uri); see deploy steps. Locally e.g. http://localhost:8080
GITLAB_USER, GITLAB_TOKENsite:install-dump, from the runner's environment; see dumps

Dumps

If drun.dump.path is missing, or --download is passed, site:install-dump downloads the site's sanitised dump from its GitLab package registry. The download needs drun.dump.path to end in .gz; a local .sql file is still imported as it is.

drun:
  dump:
    gitlab_project: group/site   # the project's ID or path on gitlab.com
    pristan_project: site        # the site's name on Pristan
export GITLAB_USER=...           # your GitLab username
export GITLAB_TOKEN=...          # a token with read_api

Both are read from the environment only. Never put them in runner.yml or runner.local.yml. They are not printed and not in the process arguments. When the runner is inside the container, pass them on: docker compose exec -e GITLAB_USER -e GITLAB_TOKEN web vendor/bin/run site:install-dump --download. The file is downloaded to a .part file first. site:install-dump needs a settings.php with the DB credentials; the database can be empty.

The first install of a new site has no config/sync yet: run vendor/bin/drush site:install <profile>, then vendor/bin/drush config:export. site:install-clean needs a complete config/sync.

Project commands

drun:
  commands:
    - App\Robo\ProjectCommands

The class must be autoloadable by the project's composer.json. Extend Robo\Tasks and use Syp\Drun\Robo\Traits\ExecTrait to get the exec prefix.

Git hooks

drun.git.hooks maps each hook name to a list of shell commands, run from the project root. In them, drun <command> runs vendor/bin/run <command> with PHP where git runs (a host with PHP, or inside the container), else in the running web container through docker compose exec -T web. With neither, the hook fails and says so. Other commands run as written. Run git:hooks-install again after changing it. Without PHP on the host, run it in the container: docker compose exec web vendor/bin/run git:hooks-install. Hooks that drun did not write are left alone unless you pass --force.

Upgrading from 1.x

  1. composer require --dev syp/drun:^2.0, and keep drush/drush in require: drun no longer brings it.
  2. Move what web/sites/default/settings.project.php sets into settings.site.php at the project root, except config_sync_directory, which the new settings.php sets.
  3. Delete settings.drun.php, settings.project.php and settings.php in web/sites/default (chmod u+w first if they are read-only), run vendor/bin/run drun:init and commit the new settings.php.
  4. Rename HASH_SALT to DRUPAL_HASH_SALT. DB_DRIVER and DB_PREFIX are no longer read.
  5. In runner.yml remove drun.site, drun.settings, drun.dump.source and drun.deploy. Add drun.dump.gitlab_project and drun.dump.pristan_project.
  6. Move custom drun.deploy.steps into an executable deploy.site.sh, which Pristan runs too.
  7. site:install-clean now installs from config/sync with no profile or account options.
  8. Run git:hooks-install again. In your own drun.git.hooks, write drun <command> instead of vendor/bin/run <command>, so the hooks also work where there is no PHP.

Development

composer install
bin/run qa:lint && bin/run qa:phpcs && bin/run qa:phpstan   # paths from ./runner.yml
vendor/bin/phpunit

The command tests run each case in tests/fixtures/commands/*.yml. They use a sandbox project directory under the system temp directory, and most run with --simulate. tests/CommandsTestCase.php documents the fixture keys. Most new tests need only a new YAML case, no PHP.

GitLab CI (.gitlab-ci.yml) runs these checks on PHP 8.3, 8.4 and 8.5, with both the highest and the lowest allowed dependency versions.