syp / drun
Robo-based task runner for Drupal 11 sites on Pristan: install, dump, deploy steps, QA and git hooks.
Requires
- php: >=8.3
- consolidation/robo: ^5.1
- drupal/coder: ^8.3.28
- league/container: ^4.2
- mglaman/phpstan-drupal: ^2.0
- php-parallel-lint/php-parallel-lint: ^1.4
- phpstan/extension-installer: ^1.4
- phpstan/phpstan: ^2.1.22
- phpstan/phpstan-deprecation-rules: ^2.0
Requires (Dev)
- phpunit/phpunit: ^12.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
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
| Command | Does |
|---|---|
site:install-clean | drush 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:update | Run deploy.site.sh, else drush deploy |
qa:lint / qa:phpcs / qa:phpstan | Run the tool on existing drun.qa.paths |
qa:audit | composer audit --locked |
qa:all | Run 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'.-Tis required for dump imports.drun:init --dockerwrites it to a newrunner.yml. Set it inrunner.ymlfor the whole team or inrunner.local.ymlfor 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.shat 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 withvendor/binon thePATHandSITE_ENVset,localby default. It replacesdrush deployentirely. Pristan'sexamples/deploy.site.sh.exampleis 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.
| File | Owner | Holds |
|---|---|---|
web/sites/default/settings.php | drun | Pristan's settings. Replaced on Pristan. Rewritten by drun:init --force. |
settings.site.php (project root) | project | The site's own settings, loaded by Pristan too. |
web/sites/default/settings.local.php | machine | Optional, 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 infolistsrootlessunder Security Options):APACHE_RUN_GROUP: rootinenvironment. The container's root is you on the host, so its group is yours. Do not setuser:: any other uid is a stranger on the host. - Rootful Docker:
user: "1000:1000", yourid -uandid -g. Apache anddocker compose execthen run as you; without it,execruns 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.
| Variable | Read by |
|---|---|
DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD | settings.php |
DRUPAL_HASH_SALT | settings.php |
SITE_ENV | settings.php (error level), deploy.site.sh: prod, acc, staging or local |
SITE_DOMAINS | settings.php (trusted hosts), comma-separated; the first gives DRUSH_OPTIONS_URI |
SITE_REVISION | settings.php (deployment identifier); the image sets it |
MEMCACHE_HOST, MEMCACHE_PORT | settings.php, with the memcache module |
SOLR_HOST, SOLR_PORT, SOLR_CORE | settings.site.php (search_api_solr) |
VARNISH_HOST, VARNISH_PORT | settings.site.php (the purger) |
SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASSWORD | the image's sendmail |
DRUSH_OPTIONS_URI | Drush (--uri); see deploy steps. Locally e.g. http://localhost:8080 |
GITLAB_USER, GITLAB_TOKEN | site: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
composer require --dev syp/drun:^2.0, and keepdrush/drushinrequire: drun no longer brings it.- Move what
web/sites/default/settings.project.phpsets intosettings.site.phpat the project root, exceptconfig_sync_directory, which the newsettings.phpsets. - Delete
settings.drun.php,settings.project.phpandsettings.phpinweb/sites/default(chmod u+wfirst if they are read-only), runvendor/bin/run drun:initand commit the newsettings.php. - Rename
HASH_SALTtoDRUPAL_HASH_SALT.DB_DRIVERandDB_PREFIXare no longer read. - In
runner.ymlremovedrun.site,drun.settings,drun.dump.sourceanddrun.deploy. Adddrun.dump.gitlab_projectanddrun.dump.pristan_project. - Move custom
drun.deploy.stepsinto an executabledeploy.site.sh, which Pristan runs too. site:install-cleannow installs fromconfig/syncwith no profile or account options.- Run
git:hooks-installagain. In your owndrun.git.hooks, writedrun <command>instead ofvendor/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.