{"id":13561597,"url":"https://github.com/lullabot/drainpipe","last_synced_at":"2026-05-04T16:02:30.103Z","repository":{"id":41299930,"uuid":"368204606","full_name":"Lullabot/drainpipe","owner":"Lullabot","description":null,"archived":false,"fork":false,"pushed_at":"2025-04-23T19:27:26.000Z","size":89909,"stargazers_count":36,"open_issues_count":67,"forks_count":14,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-04-24T06:54:09.049Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Lullabot.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-05-17T13:55:52.000Z","updated_at":"2025-04-23T13:22:52.000Z","dependencies_parsed_at":"2023-12-19T05:03:33.377Z","dependency_job_id":"67c45583-0bbe-4055-8775-c49c0b1f6fc4","html_url":"https://github.com/Lullabot/drainpipe","commit_stats":{"total_commits":130,"total_committers":13,"mean_commits":10.0,"dds":0.3076923076923077,"last_synced_commit":"4b681a012cfb2355d025c1f1ea07727cd69dfd9d"},"previous_names":[],"tags_count":59,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lullabot%2Fdrainpipe","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lullabot%2Fdrainpipe/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lullabot%2Fdrainpipe/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Lullabot%2Fdrainpipe/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Lullabot","download_url":"https://codeload.github.com/Lullabot/drainpipe/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250580706,"owners_count":21453531,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T13:00:58.967Z","updated_at":"2026-05-04T16:02:30.080Z","avatar_url":"https://github.com/Lullabot.png","language":"PHP","funding_links":[],"categories":["Tools"],"sub_categories":[],"readme":"# Drainpipe\n\nDrainpipe is a composer package which provides build tool and testing helpers\nfor a Drupal site, including:\n\n- Site and database updates\n- Artifact packaging for deployment to a hosting provider\n- Automated testing setup\n- Integration with DDEV\n- CI integration\n\n## Installation\n\n```sh\ncomposer config extra.drupal-scaffold.gitignore true\ncomposer config --json extra.drupal-scaffold.allowed-packages \"[\\\"lullabot/drainpipe\\\", \\\"lullabot/drainpipe-dev\\\"]\"\ncomposer require lullabot/drainpipe\ncomposer require lullabot/drainpipe-dev --dev\n```\n\nand if using DDEV, restart to enable the added features:\n```sh\nddev restart\n```\n\n### Taskfile\n\nDrainpipe will scaffold out various files, most importantly a `Taskfile.yml` in\nthe root of your repository. [Task](https://taskfile.dev/) is a task runner / build tool\nthat aims to be simpler and easier to use than, for example, GNU Make. Since\nit's written in Go, Task is just a single binary and has no other dependencies.\nIt's also cross-platform with everything running through the same [shell interpreter](https://github.com/mvdan/sh).\n\nDrainpipe installs the Task binary for you, no matter if you are using DDEV or\nnot. If you already have Task installed system-wide and are not using DDEV, your\ninstalled binary will be linked from the `vendor/bin` directory.\n\nYou can see which tasks are available after installation by running\n`./vendor/bin/task --list` or `ddev task --list` if you're running DDEV. To get\nmore information on a specific task e.g. what parameters it takes, you can run\n`task [task name] --summary`.\n\nYour `Taskfile.yml` can be validated with JSON Schema:\n```\ncurl -O https://taskfile.dev/schema.json\nnpx ajv-cli validate -s schema.json -d scaffold/Taskfile.yml\n```\n\nSee [`.github/workflows/ValidateTaskfile.yml`](.github/workflows/ValidateTaskfile.yml)\nfor an example of this in use.\n\n### Overriding files provided by drainpipe\n\nDrupal scaffolds are core files automatically placed and updated in the project\nroot by `drupal/core-composer-scaffold` ([documentation](https://www.drupal.org/docs/develop/using-composer/using-drupals-composer-scaffold#toc_4)).\n\nScaffold files provided by Drainpipe are located in the main `scaffold` directory.\n\nTo determine where a file is placed, edit the `extra.drupal-scaffold` section in\n`composer.json`. Check how each scaffolded file is defined in `/src/ScaffoldInstallerPlugin.php`.\n\nA specific file scaffolding can be disabled by mapping it to false under the\n`extra.drupal-scaffold.file-mapping` in the project `composer.json` file. This\nprevents it from being created or overriden when running `composer install` or\n`composer update`. Note Drainpipe workflows cannot be overridden.\n\n```\n{\n  \"extra\": {\n    \"drupal-scaffold\": {\n      \"file-mapping\": {\n        \"[web-root]/robots.txt\": false\n      }\n    }\n  }\n}\n```\n\n### Binaries\n\nIf you receive an error such as `exec format error: ./vendor/bin/task`, then\nyou may have the wrong binary for your architecture. If your architecture\nwasn't detected correctly, please open an issue with the output of `php -r \"echo php_uname('m');\"`,\nalong with information on your hardware and operating system.\n\nYou can override the platform and processor with environment variables `DRAINPIPE_PLATFORM`\nand `DRAINPIPE_PROCESSOR`. Valid platform values are `linux`, `darwin`, or `windows`,\nand processors are `386`, `amd64`, or `arm64`. These correspond to builds of\nupstream dependencies e.g. https://github.com/go-task/task/releases\n\n### Tugboat\n\nThe Tugboat configuration file is not a static file; it is dynamically generated\nbased on your `.ddev/config.yaml` file. To see the implementation details, check\n[/src/TugboatConfigPlugin.php](https://github.com/Lullabot/drainpipe/blob/main/src/TugboatConfigPlugin.php).\n\n### Node JS\n\nAll Drainpipe components (DDEV, Github Actions, Tugboat) are configured to use\nthe same Node JS major version. This is set in the `.nvmrc` file, which is\nscaffolded from the Drainpipe's root directory, but can be overriden to use a\ndifferent Node version.\n\n## Renovate Presets\n\nIf you are using [Renovate](https://docs.renovatebot.com/) (for automated dependency updates) you can use/extend our Drupal presets by doing the following:\n\n```\n{\n  \"extends\": [\n    \"group:drupal-core\"\n ]\n}\n```\n\nThis preset provides safe automation with flexibility and control for teams maintaining Drupal applications, minimizing risk by requiring approval for major changes while accelerating security patches through the automerging of minor updates.\n\n\n## Database Updates\n\nThe `drupal:update` command runs [`drush deploy`](https://www.drush.org/12.x/deploycommand/)\ndirectly, per the [Lullabot ADR on Drupal build steps](https://architecture.lullabot.com/adr/20260313-drupal-build-steps/).\n\n```\ndrush deploy\n```\n\nSites that need to customize the deploy steps (e.g. running `config:import`\ntwice, or running `drush cron` after deploy) should replace the `drush deploy`\ncommand using a site-level Drush command file. See the\n[ADR](https://architecture.lullabot.com/adr/20260313-drupal-build-steps/) for a\nfull example.\n\n## .env support\n\nDrainpipe will add `.env` file support for managing environment variables.\n\n**This is only used for locals** - other environments such as CI and production\nshould use their native environment variable mechanisms.\n\nThis consists of:\n- Creation of a `.env` and `.env.defaults` file\n- Default `Taskfile.yml` contains [dotenv support](https://taskfile.dev/usage/#env-files)\n  _note: real environment variables will override these_\n- Drupal integration via [`vlucas/phpdotenv`](https://packagist.org/packages/vlucas/phpdotenv)\n  To enable this, add the following to your `composer.json`:\n  ```\n  \"autoload-dev\":\n  {\n    \"files\": [\n      \"vendor/lullabot/drainpipe/scaffold/env/dotenv.php\"\n    ]\n  },\n  ```\n  **You will need to restart DDEV if you make any changes to `.env` or`.env.defaults`**\n\n## SASS Compilation\n\nThis compiles CSS assets using [Sass](https://sass-lang.com/). It also supports\nthe following:\n- Globbing\n  ```\n  // Base\n  @use \"sass/base/**/*\";\n  ```\n- [Modern Normalizer](https://www.npmjs.com/package/modern-normalize)\n- Autoprefixer configured through a [`.browserslistrc`](https://github.com/postcss/autoprefixer)\n  file in the project root\n\n### Setup\n- Add @lullabot/drainpipe-sass to your project\n  `yarn add @lullabot/drainpipe-sass` or `npm install @lullabot/drainpipe-sass`\n- Edit `Taskfile.yml` and add `DRAINPIPE_SASS` in the `vars` section\n  ```\n  vars:\n    DRAINPIPE_SASS: |\n      web/themes/custom/mytheme/style.scss:web/themes/custom/mytheme/style.css\n      web/themes/custom/myothertheme/style.scss:web/themes/custom/myothertheme/style.css\n  ```\n- Run `task sass:compile` to check it works as expected\n- Run `task sass:watch` to check file watching works as expected\n- Add the task to a task that compiles all your assets e.g.\n  ```\n  assets:\n    desc: Builds assets such as CSS \u0026 JS\n    cmds:\n      - yarn install --immutable --immutable-cache --check-cache\n      - task: sass:compile\n      - task: javascript:compile\n  assets:watch:\n    desc: Builds assets such as CSS \u0026 JS, and watches them for changes\n    deps: [sass:watch, javascript:watch]\n  ```\n\n## JavaScript Compilation\n\nJavaScript bundling support is via [esbuild](https://esbuild.github.io/).\n\n### Setup\n\n- Add @lullabot/drainpipe-javascript to your project\n  `yarn add @lullabot/drainpipe-javascript` or `npm install @lullabot/drainpipe-javascript`\n- Edit `Taskfile.yml` and add `DRAINPIPE_JAVASCRIPT` in the `vars section`\n  ```\n  DRAINPIPE_JAVASCRIPT: |\n    web/themes/custom/mytheme/script.js:web/themes/custom/mytheme/script.min.js\n    web/themes/custom/myotherthemee/script.js:web/themes/custom/myothertheme/script.min.js\n  ```\n  Source and target need to have the same basedir (web or docroot) due to being\n  unable to provide separate entryNames.\n  See https://github.com/evanw/esbuild/issues/224\n- Run `task javascript:compile` to check it works as expected\n- Run `task javascript:watch` to check file watching works as expected\n- Add the task to a task that compiles all your assets e.g.\n  ```\n  assets:\n    desc: Builds assets such as CSS \u0026 JS\n    cmds:\n      - yarn install --immutable --immutable-cache --check-cache\n      - task: sass:compile\n      - task: javascript:compile\n  assets:watch:\n    desc: Builds assets such as CSS \u0026 JS, and watches them for changes\n    deps: [sass:watch, javascript:watch]\n  ```\n\n## Testing\n\nThis is provided by the separate [drainpipe-dev](https://github.com/Lullabot/drainpipe-dev)\npackage (so the development/testing dependencies aren't installed in production\nbuilds).\n\n### Static Tests\n\nAll the below static code analysis tests can be run with `task test:static`\n\n| Test Type | Task Command             | Description                                                                                                                                                                                                                                                            |\n|-----------|--------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| Security  | task test:security       | Runs security checks for composer packages against the [FriendsOfPHP Security Advisory Database](https://github.com/FriendsOfPHP/security-advisories) and Drupal core and contributed modules against [Drupal's Security Advisories](https://www.drupal.org/security). |\n| Lint      | task test:lint           | - YAML lint on `.yml` files in the `web` directory\u003cbr /\u003e- Twig lint on files in `web/modules`, `web/profiles`, and `web/themes`\u003cbr /\u003e- `composer validate`\u003cbr /\u003eThese cannot currently be customised. See [#9](https://github.com/Lullabot/drainpipe-dev/issues/9).    |\n| PHPStan   | task test:phpstan        | Runs [PHPStan](https://phpstan.org/) with [mglaman/phpstan-drupal](https://github.com/mglaman/phpstan-drupal) on `web/modules/custom`, `web/themes/custom`, and `web/sites` at level 6.                                                                                 |\n| PHPUnit   | task test:phpunit:static | Runs Unit tests in `web/modules/custom/**/tests/src/Unit` and `test/phpunit/**/Unit`                                                                                                                                                                                   |\n| PHPCS     | task test:phpcs          | Runs PHPCS with Drupal coding standards provided by [Coder module](https://www.drupal.org/project/coder                                                                                                                                                                |\n\n\n#### PHPStan Baseline for Existing Codebases\n\nProjects with pre-existing violations can generate a baseline file to suppress\nthem, so CI only fails on new code:\n\n```\ntask test:phpstan:generate-baseline\ngit add phpstan.neon phpstan-baseline.neon\ngit commit -m \"Add PHPStan level 6 baseline\"\n```\n\nThis creates `phpstan-baseline.neon` (the suppressed violations) and\n`phpstan.neon` (which includes both `phpstan.neon.dist` and the baseline).\nCommit both files. The baseline can be regenerated at any time as legacy\nviolations are resolved.\n\n#### Altering PHP_CodeSniffer Configuration\n\n- Create `phpcs.xml` to override the `phpcs.xml.dist` file with overrides being done in:\n  ```\n  \u003crule ref=\"phpcs.xml.dist\"\u003e\n  \u003c/rule\u003e\n  ```\n- Edit `phpcs.xml` in the root of your project, e.g. to add an exclude pattern:\n  ```\n  \u003c!-- Custom excludes --\u003e\n  \u003cexclude-pattern\u003eweb/sites/sites.php\u003c/exclude-pattern\u003e\n  ```\n\n### Functional Tests\n\nFunctional tests require some mechanism of creating a functing Drupal site to\ntest against. All the below tests can be run with `task test:functional`\n\n#### PHPUnit\n`task test:phpunit:functional`\n\nRuns PHPUnit tests in:\n- `web/modules/custom/**/tests/src/Kernel`\n- `test/phpunit/**/Kernel`\n- `web/modules/custom/**/tests/src/Functional`\n- `test/phpunit/**/Functional`\n- `web/modules/custom/**/tests/src/FunctionalJavaScript`\n- `test/phpunit/**/FunctionalJavaScript`\n\nYou will need to make sure you have a working Drupal site before you're\nable to run these.\n\n### Autofix\n\n`task test:autofix` attempts to autofix any issues discovered by tests.\nCurrently, this is just fixing PHPCS errors with PHPCBF.\n\n## Hosting Provider Integration\n\n### Generic\n\nGeneric helpers for deployments can be found in [`tasks/snapshot.yml`,](tasks/snapshot.yml)\n[`tasks/deploy.yml`](tasks/deploy.yml), and [`tasks/drupal.yml`](tasks/drupal.yml)\n\n|                                    |                                                                               |\n|------------------------------------|-------------------------------------------------------------------------------|\n| `task deploy:git`                  | Pushes a directory to a git remote                                            |\n| `task drupal:composer:development` | Install composer dependencies                                                 |\n| `task drupal:composer:production`  | Install composer dependencies without devDependencies                         |\n| `task drupal:export-db`            | Exports a database fetched with a *:fetch-db command                          |\n| `task drupal:import-db`            | Imports a database fetched with a *:fetch-db command                          |\n| `task drupal:install`              | Runs the site installer                                                       |\n| `task drupal:maintenance:off`      | Turn off Maintenance Mode                                                     |\n| `task drupal:maintenance:on`       | Turn on Maintenance Mode                                                      |\n| `task drupal:update`               | Run Drupal update tasks after deploying new code                              |\n| `task snapshot:archive`            | Creates a snapshot of the current working directory and exports as an archive |\n| `task snapshot:directory`          | Creates a snapshot of the current working directory                           |\n\n#### Importing/Exporting Databases\n\nDatabases are by default fetched to `/var/www/html/files/db/db.sql.gz`, this can\nbe overridden with a [variable](https://taskfile.dev/usage/#variables) in Task:\n```\n`task drupal:import-db DB_DIR=\"/var/www/htdocs\"`\n```\n\n#### Snapshots\n\nWhen creating a snapshot of the current working directly files can be excluded\nusing a `.drainpipeignore` file in the root of the repository that uses the same\nformat as `.gitignore`, e.g.\n```\n# Files that won't be deployed to Pantheon\n/.ddev\n/.github\n/.yarn\n/files/db\n/tests\n/.env\n/.env.defaults\n/README.md\n/Taskfile.yml\n*.sql\n*.sql.gz\n```\n\nThis folder can then be deployed to a remote service either as an archive, or\npushed to a git remote with `task deploy:git`.\n\n### Pantheon\n\nPantheon specific tasks are contained in [`tasks/pantheon.yml`](tasks/pantheon.yml).\nAdd the following to your `Taskfile.yml`'s `includes` section to use them:\n```yml\nincludes:\n  pantheon: ./vendor/lullabot/drainpipe/tasks/pantheon.yml\n```\n|                          |                                                                             |\n|--------------------------|-----------------------------------------------------------------------------|\n| `task pantheon:fetch-db` | Fetches a database from Pantheon. Set `PANTHEON_SITE_ID` in Taskfile `vars` |\n|                          | and optionally `ENVIRONMENT` to override the default value of `live`        |\n\nSee below for CI specific integrations for hosting providers.\n\nWhen using Pantheon with Drainpipe, the Pantheon site should be configured to\noverride some of Pantheon's default behaviors. Because Drainpipe installs\ncomposer dependencies, Pantheon's [Integrated Composer](https://docs.pantheon.io/guides/integrated-composer)\nshould be disabled. Add `build_step: false` to your pantheon.yml file:\n```yml\n---\napi_version: 1\nbuild_step: false\n```\n\nAdditionally, Pantheon sites start with \"[Autopilot](https://docs.pantheon.io/guides/autopilot)\",\nwhich provides updates from the Drupal upstream. Usually this feature\nconflicts with an external Git repository such as GitHub or GitLab. It is\nrecommended to disable this by setting an empty upstream with terminus.\n```\nddev ssh\nterminus site:upstream:set [site_name] empty\n```\n\n#### Pantheon Systems: Drupal Integrations\n\n`pantheon-systems/drupal-integrations` is a package that provides essential Pantheon functionality. It is strongly recommended to install it.\n\n```\ncomposer require pantheon-systems/drupal-integrations\n```\n\n### Acquia\n\nAcquia specific tasks are contained in [`tasks/acquia.yml`](tasks/acquia.yml).\nAdd the following to your `Taskfile.yml`'s `includes` section to use them:\n```yml\nincludes:\n  acquia: ./vendor/lullabot/drainpipe/tasks/acquia.yml\n```\n|                        |                                                                                                                                                                |\n|------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `task acquia:fetch-db` | Fetches a database from Acquia. Set `ACQUIA_ENVIRONMENT_ID` in Taskfile `vars`, along with `ACQUIA_API_KEY` and `ACQUIA_API_SECRET` as environment variables |\n\n\u003e ⚠️ **Beta Notice**: The Acquia integration is currently in beta. While we strive to maintain stability, you may encounter unexpected issues. Please report any problems you encounter through our issue tracker.\n\nTo enable auto configuration of Acquia Cloud settings:\n```json\n\"extra\": {\n    \"drainpipe\": {\n        \"acquia\": {\n            \"settings\": true\n        },\n    }\n}\n```\n\n## GitHub Actions Integration\n\nAdd the following to `composer.json` for generic GitHub Actions that will be\ncopied to `.github/actions/drainpipe` in your project:\n```json\n\"extra\": {\n  \"drainpipe\": {\n    \"github\": []\n  }\n}\n```\n\nThey are composite actions which can be used in any of your workflows e.g.\n```\n- uses: ./.github/actions/drainpipe/set-env\n\n- name: Install and Start DDEV\n  uses: ./.github/actions/drainpipe/ddev\n  with:\n    git-name: Drainpipe Bot\n    git-email: no-reply@example.com\n    ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}\n    ssh-known-hosts: ${{ secrets.SSH_KNOWN_HOSTS }}\n```\n\nTests can be run locally with [act](https://github.com/nektos/act):\n```\n# Turn off ddev so it doesn't get confused between your real host and the\n# \"host\" inside of act. Do this again and when done and you want to run\n# the app on your host again.\nddev poweroff\n\n# Windows\nact -P ubuntu-latest=ghcr.io/catthehacker/ubuntu:runner-latest -j Static-Tests\n\n# Mac with Docker Desktop\nact --container-options \"--group-add $(stat -f %g /var/run/docker.sock)\" \\\n  -P ubuntu-latest=ghcr.io/catthehacker/ubuntu:runner-latest \\\n  -j Static-Tests\n\n# Mac with OrbStack\nexport DOCKER_HOST=$(docker context inspect --format '{{.Endpoints.docker.Host}}')\nact --container-options \"--group-add $(stat -f %g ~/.orbstack/run/docker.sock)\" \\\n  -P ubuntu-latest=ghcr.io/catthehacker/ubuntu:runner-latest \\\n  -j Static-Tests\n\n# Mac with Lima\n# Act gets confused with socket permissions. Run act from inside the lima VM.\n# Run `lima` to shell into the VM, then install act following the docs or from\n# https://github.com/nektos/act/releases.\n# As well, comment out the chown in `.github/actions/drainpipe/ddev/action.yml`.\nact  --container-options \"--group-add $(stat -c %g /var/run/docker.sock)\" \\\n  -P ubuntu-latestt=ghcr.io/catthehacker/ubuntu:runner-latest \\\n  -j Static-Tests\n\n# Linux\nact --container-options \"--group-add $(stat -c %g /var/run/docker.sock)\" \\\n  -P ubuntu-latest=ghcr.io/catthehacker/ubuntu:runner-latest \\\n  -j Static-Tests\n\n# Using an alternate image like WarpBuild\n`-P warp-ubuntu-latest-x64-2x-spot=ghcr.io/catthehacker/ubuntu:runner-latest`.\n```\n\n### Tests\n\nWorkflows for running static and functional tests can be added with the following\nconfiguration:\n```json\n\"extra\": {\n  \"drainpipe\": {\n    \"github\": [\"TestStatic\", \"TestFunctional\"]\n  }\n}\n```\n\nThe build process for the functional tests will use `task build:ci:functional`,\nfalling back to `task build:dev`, and then `task build`. The static tests\nshould not require a build step.\n\nThese workflow files will continue to be managed by Drainpipe and cannot be\noverridden. If you wish to do so then it's recommended you maintain your own\nworkflows for testing.\n\n### Security\n\n```json\n\"extra\": {\n  \"drainpipe\": {\n    \"github\": [\"Security\"]\n  }\n}\n```\n\nRuns security checks for composer packages and Drupal contrib, as well as posting\na diff of `composer.lock` as a review comment.\n\nSecurity checks rely on `composer audit`. This means `test:security` task will\nexit with error if your dependencies have known vulnerabilities. Sometimes, this\nis not desirable: think of a component which is required by Drupal core - you\ncan not upgrade it directly, because it is pinned to a specific version by Drupal\nin the first place. To prevent these kind of situations from failing the security\ncheck, you can configure `composer audit` to ignore specific vulnerabilities\nwhen scanning your dependencies. To do so, the simplest way is to modify your\n`composer.json` file to add a list of vulnerabilities that should not trigger an\nerror when running `test:security`:\n\n```json\n\"config\": {\n    \"audit\": {\n        \"ignore\": [\"CVE-1234\", \"GHSA-xx\", \"PKSA-yy\"]\n    }\n}\n```\n\nFor more details about how to configure the ignore feature for `composer audit`,\ncheck the [Composer docs](https://getcomposer.org/doc/06-config.md#ignore).\n\nThe Security workflow also includes a [Zizmor](https://woodruffw.github.io/zizmor/)\nstatic analysis job (`ZizmorAnalysis`) that scans your GitHub Actions workflow\nfiles for security issues. Zizmor results are uploaded to GitHub's code scanning\ndashboard, which requires [Code Security](https://docs.github.com/en/code-security/getting-started/github-security-features)\nto be enabled for your repository. If it is not enabled, the `ZizmorAnalysis`\njob will fail with: _\"Code Security must be enabled for this repository to use\ncode scanning.\"_\n\nThe Security workflow also includes a [Gitleaks](https://github.com/gitleaks/gitleaks)\njob (`Gitleaks`) that scans every PR and push to the default branch for accidentally\ncommitted secrets — API keys, tokens, credentials, and similar sensitive values.\nFindings are uploaded to GitHub's code scanning dashboard under the **Gitleaks**\ncategory.\n\n**Suppressing false positives**: Drainpipe scaffolds a `.gitleaks.toml` at the\nproject root that pre-configures allowlists for patterns common in Drupal\nprojects (contributed modules, Drupal core, GitHub Actions template expressions,\nand placeholder values). Add `[[allowlists]]` blocks to that file to suppress\nadditional false positives specific to your project. Use `[extend]` with\n`useDefault = true` to inherit all default rules and layer your allowlists on top:\n\n```toml\n[[allowlists]]\ndescription = \"Allow example values in documentation\"\nregexes = [\n  '''(?i)example''',\n  '''(?i)replace[-_.]?me''',\n]\n\n[[allowlists]]\ndescription = \"Allow test fixtures\"\npaths = [\n  '''tests/fixtures/''',\n]\n```\n\n**Recommended: one-time full history scan**: When first adopting Gitleaks, run a\nscan of your full git history to check for secrets that may already be committed:\n\n```sh\n# Ensure you have a full clone (not shallow)\ngit fetch --unshallow\n\n# Scan full history and save results to a JSON report\ngitleaks detect --source . --report-format json --report-path gitleaks-history.json\n```\n\nReview `gitleaks-history.json` and for each finding: rotate any credentials that\nare still valid; for false positives, add the pattern to `.gitleaks.toml`. Note\nthat rewriting git history to remove secrets is disruptive — rotating the\ncredential is the most effective remediation.\n\n### NPM \u0026 Yarn Lockfile Diff\n\nPost a sticky comment in the Pull Request with a markdown table of any changes\ndetected in `yarn.lock` or `package-lock.json` files.\n\n```json\n\"extra\": {\n    \"drainpipe\": {\n        \"github\": [\"LockfileDiff\"]\n    }\n}\n```\n\n### Pantheon\n\nTo scaffold Pantheon composite actions for use in your own workflows (without a managed review app\nworkflow), add `\"Actions\"`:\n\n```json\n\"extra\": {\n    \"drainpipe\": {\n        \"github\": {\n            \"pantheon\": [\"Actions\"]\n        }\n    }\n}\n```\n\nThis scaffolds `.github/actions/drainpipe/pantheon/` (setup-terminus, push, clone-env, update,\nreview) along with `pantheon.yml` and `.drainpipeignore`, without installing any workflow file.\nUse this when you manage your own deployment workflow and want Drainpipe's composite actions as\nbuilding blocks.\n\nTo enable deployment of Pantheon Review Apps (Multidev environments per pull request):\n\n- Add the following to `composer.json`:\n  ```json\n  \"extra\": {\n      \"drainpipe\": {\n          \"github\": {\n              \"pantheon\": [\"ReviewApps\"]\n          }\n      }\n  }\n  ```\n  `ReviewApps` implicitly includes `Actions` — you do not need to list both.\n- Run `composer install` to install the workflow to `.github/workflows`\n- [Create a `build multidev` label](https://docs.github.com/en/issues/using-labels-and-milestones-to-track-work/managing-labels#creating-a-label)\n  in your GitHub repository. The workflow triggers when this label is added to a pull request,\n  and re-runs on subsequent commits while the label is present.\n  - If you want the multidev build to run on **every** pull request automatically, add the\n    `build multidev` label to your [pull request template](https://docs.github.com/en/communities/using-templates-to-encourage-useful-issues-and-pull-requests/creating-a-pull-request-template-for-your-repository).\n- Add the following [variables to your GitHub repository](https://docs.github.com/en/actions/learn-github-actions/variables#creating-configuration-variables-for-a-repository):\n    - `PANTHEON_SITE_NAME` The canonical site name in Pantheon\n    - `TERMINUS_PLUGINS` (optional) Comma-separated list of Terminus plugins to be available\n    - `TERMINUS_TIMEOUT_LIMIT` (optional) Number of seconds that terminus will wait until timeout. Defaults to 600\n    - `PANTHEON_CLONE_FROM` (optional) The environment to clone from when creating multidev sites. Defaults to 'live'\n    - `PANTHEON_SKIP_WIPE_MULTIDEV` (optional) Set to 'true' to skip wiping the multidev environment on each push, preserving its database state. Defaults to 'false'\n- Add the following [secrets to your GitHub repository](https://docs.github.com/en/codespaces/managing-codespaces-for-your-organization/managing-development-environment-secrets-for-your-repository-or-organization#adding-secrets-for-a-repository):\n    - `TERMINUS_MACHINE_TOKEN` See https://pantheon.io/docs/terminus/install#machine-token\n    - `SSH_PRIVATE_KEY` A private key of a user which can push to Pantheon\n    - `SSH_KNOWN_HOSTS` The result of running `ssh-keyscan -H -p 2222 codeserver.dev.$PANTHEON_SITE_ID.drush.in`\n    - `PANTHEON_REVIEW_USERNAME` (optional) A username for HTTP basic auth\n    - `PANTHEON_REVIEW_PASSWORD` (optional) The password to lock the site with\n    - `PANTHEON_REVIEW_RUN_INSTALLER` (optional) Set to `\"true\"` to run `site:install --existing-config` instead of `drupal:update` when deploying\n\n### Acquia\n\nTo add Acquia specific GitHub actions, add the following to `composer.json`:\n  ```json\n  \"extra\": {\n      \"drainpipe\": {\n          \"github\": {\n              \"acquia\": [\"Deploy\"]\n          }\n      }\n  }\n  ```\nThen run `composer install`. A Deploy to Acquia workflow at `.github/workflows/AcquiaDeploy.yml` will be added (with its dependent actions).\n\nAfter the Github Actions Integration is merged, you can deploy to Acquia using the UI or the Github CLI (gh).\n\n**Github repository settings**\n\nIn your Github repository settings you need to create the following:\n- Repository Variables\n  - To identify the project we are deploying (same value of the AH_SITE_GROUP environment value)\n    - ACQUIA_SITE_GROUP\n- Repository Secrets\n  - To use Acquia CLI (acli) to interact with [Acquia's Cloud Platform](https://docs.acquia.com/acquia-cloud-platform/add-ons/acquia-cli/start)\n    - ACQUIA_API_KEY\n    - ACQUIA_API_SECRET\n  - To push code to your Acquia repository\n    - ACQUIA_SSH_PRIVATE_KEY\n\n**Task settings**\n\nWhen a deployment is made, you must run your own code _before_ and _after_ the deployment. To do so, define your in your `/Taskfile.yml` the following:\n\n- **acquia:deploy:before**\n\n  ```\n    acquia:deploy:before\n      desc: \"Before the code is deployed to Acquia, run these commands on the GitHub Actions runner\"\n      cmds:\n        # This task is run on the GitHub Actions runner\n        # It is a good moment to run tasks like build\n        # so we can later create the artifact for the Acquia environment.\n        - task: build\n  ```\n- **acquia:deploy:after**\n\n  Runs after the code is switched on the Acquia environment and before\n  `drupal:update` is called automatically by Drainpipe. Use this for any steps\n  that must happen between the code switch and the Drupal update, such as\n  enabling maintenance mode or syncing files from another environment.\n\n  ```\n    acquia:deploy:after:\n      desc: \"After the code is switched on the Acquia environment, run these commands\"\n      cmds:\n        # This task runs on the Acquia environment. The site alias is passed\n        # automatically so commands like drush run against the correct environment.\n        - task: drupal:maintenance:on\n  ```\n\n## GitLab CI Integration\n\nAdd the following to `composer.json` for GitLab helpers:\n```json\n\"extra\": {\n  \"drainpipe\": {\n    \"gitlab\": []\n  }\n}\n```\n\nThis will import [`scaffold/gitlab/Common.gitlab-ci.yml`](scaffold/gitlab/Common.gitlab-ci.yml),\nwhich provides helpers that can be used in GitLab CI with [includes and\nreferences](https://docs.gitlab.com/ee/ci/yaml/yaml_specific_features.html#reference-tags),\nor `scaffold/gitlab/DDEV.gitlab-ci.yml` if you are using DDEV.\n\n```\ninclude:\n  - local: '.gitlab/drainpipe/DDEV.gitlab-ci.ymll'\n\nvariables:\n  DRAINPIPE_DDEV_GIT_EMAIL: drainpipe-bot@example.com\n  DRAINPIPE_DDEV_GIT_NAME: Drainpipe Bot\n\nbuild:\n  stage: build\n  interruptible: true\n  script:\n    - !reference [.drainpipe_setup_ddev, script]\n    - composer install\n    - ddev restart\n    - ddev drush site:install minimal -y\n    - echo \"\\$settings['config_sync_directory'] = '../config';\" \u003e\u003e web/sites/default/settings.php\n    - ddev drush config:export -y\n    - ddev task update\n```\n\nAvailable variables are:\n\n| Variable                          |                                                                                                                                    |\n|-----------------------------------|------------------------------------------------------------------------------------------------------------------------------------|\n| DRAINPIPE_DDEV_SSH_PRIVATE_KEY    | SSH private key used for e.g. committing to git                                                                                    |\n| DRAINPIPE_DDEV_SSH_KNOWN_HOSTS    | The result of running e.g. `ssh-keyscan -H -p 2222 codeserver.dev.$PANTHEON_SITE_ID.drush.in`                                              |\n| DRAINPIPE_DDEV_GIT_EMAIL          | E-mail address to use for git commits                                                                                              |\n| DRAINPIPE_DDEV_GIT_NAME           | Name to use for git commits                                                                                                        |\n| DRAINPIPE_DDEV_COMPOSER_CACHE_DIR | Set to \"false\" to disable composer cache dir, or another value to override the default location of .ddev/.drainpipe-composer-cache |\n| DRAINPIPE_DDEV_VERSION            | Install a specific version of DDEV instead of the latest                                                                           |\n\n### Security\n\nRuns `composer audit` and posts a composer lock diff as a merge request comment.\nRequires `GITLAB_ACCESS_TOKEN` variable to be set with `api` scope.\n\n```json\n\"extra\": {\n    \"drainpipe\": {\n        \"gitlab\": [\"Security\"]\n    }\n}\n```\n\n### Pantheon\n\nTo enable deployment of Pantheon Review Apps (Multidev environments per merge request):\n\n- Add the following to `composer.json`:\n  ```json\n  \"extra\": {\n      \"drainpipe\": {\n          \"gitlab\": {\n              \"pantheon\": [\"Deploy\", \"ReviewApps\"]\n          }\n      }\n  }\n  ```\n  Use `\"Deploy\"` alone if you only want the Terminus setup helpers without the full review app\n  pipeline. Use both `\"Deploy\"` and `\"ReviewApps\"` for full Multidev support.\n- Run `composer install` to install the CI files to `.drainpipe/gitlab/`. If no `.gitlab-ci.yml`\n  exists, an example one will be created for you.\n- Add the following [variables to your GitLab repository](https://docs.gitlab.com/ee/ci/variables/#for-a-project):\n  - `PANTHEON_SITE_NAME` The canonical site name in Pantheon\n  - `PANTHEON_SITE_ID` The Pantheon site UUID, used to construct the SSH remote URL\n  - `PANTHEON_GIT_REMOTE` The Pantheon git remote URL e.g. `ssh://codeserver.dev.$PANTHEON_SITE_ID@codeserver.dev.$PANTHEON_SITE_ID.drush.in:2222/~/repository.git`\n  - `TERMINUS_MACHINE_TOKEN` See https://pantheon.io/docs/terminus/install#machine-token (enable the _Mask variable_ checkbox)\n  - `SSH_PRIVATE_KEY` A private key of a user which can push to Pantheon (enable the _Mask variable_ checkbox)\n  - `SSH_KNOWN_HOSTS` The result of running `ssh-keyscan -H -p 2222 codeserver.dev.$PANTHEON_SITE_ID.drush.in` (enable the _Mask variable_ checkbox)\n  - `GIT_EMAIL` Email address to use for git commits\n  - `GIT_USERNAME` Username to use for git commits\n  - `GITLAB_ACCESS_TOKEN` A GitLab access token with `api` scope, used by the scheduled multidev cleanup job\n  - `TERMINUS_PLUGINS` (optional) Comma-separated list of Terminus plugins to be available\n  - `REVIEW_APP_BASIC_AUTH` (optional) Basic auth credentials prepended to the review app URL e.g. `user:password@`\n  - `PANTHEON_MULTIDEV_RUN_INSTALLER` (optional) Set to `\"true\"` to run `site:install --existing-config` instead of `drupal:update` when deploying\n\nThis will setup Merge Request deployment to Pantheon Multidev environments. See\n[scaffold/gitlab/gitlab-ci.example.yml] for an example. You can also just\ninclude `\"Deploy\"` which will give you helpers that you can include and reference for tasks\nsuch as setting up [Terminus](https://pantheon.io/docs/terminus). See\n[scaffold/gitlab/Pantheon.gitlab-ci.yml](scaffold/gitlab/Pantheon.gitlab-ci.yml).\n\n## Bitbucket Pipelines Integration\n\nAdd the following to `composer.json` to enable Bitbucket Pipelines support:\n\n```json\n\"extra\": {\n  \"drainpipe\": {\n    \"bitbucket\": []\n  }\n}\n```\n\n### PHP extensions\n\nThe pipeline image (`php:8.5-cli`) includes only a minimal set of PHP extensions. Before running `composer install`, `setup-php.sh` automatically detects missing extensions by running `composer check-platform-reqs` against `composer.lock` and installs them via [`mlocati/docker-php-extension-installer`](https://github.com/mlocati/docker-php-extension-installer).\n\nTo ensure an extension is installed, declare it in the `require` block of your project's `composer.json`:\n\n```json\n\"require\": {\n    \"ext-zip\": \"*\",\n    \"ext-gd\": \"*\"\n}\n```\n\nTo identify which extensions your project needs, run:\n\n```\nddev composer check-platform-reqs\n```\n\nFor extensions that cannot be declared in `composer.json` (e.g. proprietary agents such as New Relic or Tideways), set the `DRAINPIPE_PHP_EXTENSIONS` Bitbucket repository variable to a space-separated list of extension names:\n\n```\nnewrelic tideways_xhprof\n```\n\n### Acquia Deploy\n\nAcquia Deploy triggers a deployment to the Acquia `dev` environment whenever a PR is merged to `main`, equivalent to the GitHub Actions `AcquiaDeploy` workflow.\n\nTo enable Acquia Deploy on Bitbucket:\n\n- Add the following to `composer.json`:\n  ```json\n  \"extra\": {\n      \"drainpipe\": {\n          \"bitbucket\": [\"AcquiaDeploy\"],\n          \"acquia\": { \"settings\": true }\n      }\n  }\n  ```\n  If your main branch is named `master` (or anything other than `main`), set `bitbucketDeployBranch`:\n  ```json\n  \"extra\": {\n      \"drainpipe\": {\n          \"bitbucket\": [\"AcquiaDeploy\"],\n          \"bitbucketDeployBranch\": \"master\",\n          \"acquia\": { \"settings\": true }\n      }\n  }\n  ```\n  If you are using both Acquia Review Apps and Acquia Deploy, list them together:\n  ```json\n  \"bitbucket\": [\"AcquiaReviewApps\", \"AcquiaDeploy\"]\n  ```\n- Run `composer install` to scaffold the following files into your project:\n  - `bitbucket-pipelines.yml` — generated at the repository root and kept up to date on every `composer install`. Do not edit this file manually — changes will be overwritten.\n  - `Taskfile.yml` — a `bitbucket` include pointing to `vendor/lullabot/drainpipe/tasks/bitbucket.yml` is auto-injected\n\n**How it works**\n\nWhen a PR is merged to `main` the `branches.main` pipeline:\n\n1. Installs system dependencies and configures SSH\n2. Runs `composer install`\n3. Installs Acquia CLI and authenticates (`task bitbucket:acquia:setup`)\n4. Runs the pre-deploy build hook if defined (`task bitbucket:acquia:build` → `task acquia:deploy:before`)\n5. Resolves the VCS branch and remote that the Acquia `dev` environment tracks, pushes code via `task deploy:git`, and waits for the code switch to complete (`task bitbucket:acquia:push-dev`)\n6. Downloads Drush aliases, runs `task acquia:deploy:after` (if defined), then `task update` or `task drupal:update`, clears caches on all domains, and posts a commit status (`task bitbucket:acquia:update-dev`)\n\n**User-defined hooks**\n\nAdd these tasks to your project's `Taskfile.yml` as needed:\n\n```yaml\ntasks:\n  acquia:deploy:before:\n    desc: \"Runs before the Acquia deploy snapshot — compile assets, etc. Do NOT call drupal:composer:production here.\"\n    cmds:\n      - task: build\n\n  acquia:deploy:after:\n    desc: \"Runs after code is switched on Acquia — typically calls 'task drupal:update'\"\n    cmds:\n      - task: drupal:update\n        vars:\n          site: \"{{.site}}\"\n```\n\n**Bitbucket repository variables**\n\nAdd these in your Bitbucket repository settings under **Repository variables**:\n\n| Variable | Secured | Description |\n|---|---|---|\n| `ACQUIA_API_KEY` | Yes | Acquia API Key |\n| `ACQUIA_API_SECRET` | Yes | Acquia API Secret |\n| `ACQUIA_SSH_PRIVATE_KEY` | Yes | SSH private key (base64-encoded) with Acquia Git access |\n| `ACQUIA_SITE_GROUP` | No | Application/site group name (e.g. `mysite` from `mysite.dev`) |\n| `BITBUCKET_USERNAME` | No | Bitbucket username — required for commit status API calls |\n| `BITBUCKET_APP_PASSWORD` | Yes | Bitbucket App Password with `pullrequest:read` scope — required for commit status |\n| `DRAINPIPE_PHP_EXTENSIONS` | No | Space-separated list of additional PHP extensions to install beyond those declared in `composer.json` (see [PHP extensions](#php-extensions)) |\n\n\u003e Note: `BITBUCKET_COMMIT` is injected automatically by Bitbucket Pipelines and does not need to be set manually.\n\n\u003e Note: When both `AcquiaReviewApps` and `AcquiaDeploy` are configured, `composer install` generates a single merged `bitbucket-pipelines.yml` containing all pipeline sections. Do not edit this file manually — changes will be overwritten on the next `composer install`.\n\n### Acquia Review Apps\n\nAcquia Review Apps create an [Acquia Cloud Development Environment (CDE)](https://docs.acquia.com/acquia-cloud-platform/manage-apps/dev-env/) for every pull request, equivalent to Pantheon Multidev environments.\n\nTo enable Acquia Review Apps on Bitbucket:\n\n- Add the following to `composer.json`:\n  ```json\n  \"extra\": {\n      \"drainpipe\": {\n          \"bitbucket\": [\"AcquiaReviewApps\"],\n          \"acquia\": { \"settings\": true }\n      }\n  }\n  ```\n- Run `composer install` to scaffold the following files into your project:\n  - `bitbucket-pipelines.yml` — generated at the repository root and kept up to date on every `composer install`. Do not edit this file manually — changes will be overwritten.\n  - `Taskfile.yml` — a `bitbucket` include pointing to `vendor/lullabot/drainpipe/tasks/bitbucket.yml` is auto-injected\n\n**How it works**\n\nWhen a pull request is opened or updated, the `pull-requests` pipeline:\n\n1. Installs system dependencies and configures SSH\n2. Runs `composer install`\n3. Installs Acquia CLI and authenticates (`task bitbucket:acquia:setup`)\n4. Runs the pre-deploy build hook if defined (`task bitbucket:acquia:build` → `task acquia:deploy:before`)\n5. Pushes code to a PR-specific branch on Acquia, creates the CDE (`PR-{N}`) if it doesn't exist, copies the database from the source environment (default: `dev`) unless `ACQUIA_REVIEW_RUN_INSTALLER` is set, and waits for the code switch to complete (`task bitbucket:acquia:push-cde`)\n6. Downloads Drush aliases, runs `task acquia:deploy:after` (if defined) and `task update` or `task drupal:update`, then posts the environment URL as a commit status (`task bitbucket:acquia:update-cde`)\n\nStale CDEs are **not** deleted automatically on PR close. See [Scheduling the cleanup pipeline](#scheduling-the-cleanup-pipeline) below.\n\n**Bitbucket repository variables**\n\nAdd these in your Bitbucket repository settings under **Repository variables**:\n\n| Variable | Secured | Description |\n|---|---|---|\n| `ACQUIA_API_KEY` | Yes | Acquia API Key |\n| `ACQUIA_API_SECRET` | Yes | Acquia API Secret |\n| `ACQUIA_SSH_PRIVATE_KEY` | Yes | SSH private key (base64-encoded) with Acquia Git access |\n| `ACQUIA_APP_UUID` | No | Application UUID from the Acquia Cloud console |\n| `ACQUIA_SITE_GROUP` | No | Application/site group name (e.g. `mysite` from `mysite.dev`) |\n| `BITBUCKET_USERNAME` | No | Bitbucket username — required for commit status and cleanup API calls |\n| `BITBUCKET_APP_PASSWORD` | Yes | Bitbucket App Password with `pullrequest:read` scope — required for commit status and cleanup |\n| `ACQUIA_SOURCE_ENVIRONMENT` | No | Environment to copy the database from (default: `dev`) |\n| `ACQUIA_REVIEW_RUN_INSTALLER` | No | Set to `\"true\"` to run `drush site:install --existing-config` instead of copying the database |\n| `DRAINPIPE_PHP_EXTENSIONS` | No | Space-separated list of additional PHP extensions to install beyond those declared in `composer.json` (see [PHP extensions](#php-extensions)) |\n\n\u003e Note: `BITBUCKET_WORKSPACE`, `BITBUCKET_REPO_SLUG`, `BITBUCKET_COMMIT`, and `BITBUCKET_PR_ID` are injected automatically by Bitbucket Pipelines and do not need to be set manually.\n\n**Scheduling the cleanup pipeline**\n\nBitbucket does not trigger pipelines on PR close. To avoid accumulating stale CDEs, schedule the `acquia-review-apps-cleanup` custom pipeline:\n\n1. In your Bitbucket repository, go to **Repository settings → Pipelines → Schedules**\n2. Add a new schedule for the `acquia-review-apps-cleanup` custom pipeline (e.g., daily at midnight)\n\n**Known limitations**\n\n- No automatic cancel-in-progress — multiple commits to the same PR will queue rather than cancel prior runs.\n- Extra credentials required — `BITBUCKET_USERNAME` and `BITBUCKET_APP_PASSWORD` must be set manually, unlike GitHub where `GITHUB_TOKEN` is injected automatically.\n\n## Tugboat\n\nAdd the following to `composer.json` to add Tugboat configuration:\n\n```json\n{\n    \"extra\": {\n        \"drainpipe\": {\n            \"tugboat\": {}\n        }\n    }\n}\n```\n\nThen, run `ddev composer install` to generate:\n- `.tugboat/config.yml` - Complete Tugboat configuration\n- `.tugboat/scripts/` - Helper scripts (if needed)\n- `web/sites/default/settings.tugboat.php` - Drupal settings for Tugboat\n\nThe following will be autodetected based on your `.ddev/config.yml`:\n- Web server (nginx or apache)\n- PHP version\n- Database type and version\n- Nodejs version\n- Redis (Obtained with `ddev get ddev/ddev-redis`)\n- Solr (Obtained with `ddev get ddev/ddev-solr`)\n\nAdditionally, Pantheon integration can be added:\n```json\n{\n    \"extra\": {\n        \"drainpipe\": {\n            \"tugboat\": {\n                \"pantheon\": true\n            }\n        }\n    }\n}\n```\n\nThis will install [Terminus](https://docs.pantheon.io/terminus) in the Tugboat environment. Add `TERMINUS_MACHINE_TOKEN` as a [Tugboat environment variable](https://docs.tugboatqa.com/setting-up-tugboat/select-repo-settings/#set-environment-variables) and set `PANTHEON_SITE_ID` in your `Taskfile.yml` vars. Then add a `sync:tugboat` task to fetch the database during Tugboat preview builds:\n\n```\n  sync:tugboat:\n    desc: \"Fetches a database from Pantheon and imports it in Tugboat\"\n    vars:\n      DB_DIR: /var/lib/tugboat/files/db\n    cmds:\n      - task: pantheon:fetch-db\n      - task: drupal:import-db\n```\n\nSimilarly, Acquia integration can be added:\n```json\n{\n    \"extra\": {\n        \"drainpipe\": {\n            \"tugboat\": {\n                \"acquia\": true\n            }\n        }\n    }\n}\n```\n\nThis will install [Acquia CLI (acli)](https://docs.acquia.com/acquia-cloud-platform/add-ons/acquia-cli/start) in the Tugboat environment. Add `ACQUIA_API_KEY` and `ACQUIA_API_SECRET` as [Tugboat environment variables](https://docs.tugboatqa.com/setting-up-tugboat/select-repo-settings/#set-environment-variables) and set `ACQUIA_ENVIRONMENT_ID` in your `Taskfile.yml` vars. Then add a `sync:tugboat` task:\n\n```\n  sync:tugboat:\n    desc: \"Fetches a database from Acquia and imports it in Tugboat\"\n    vars:\n      DB_DIR: /var/lib/tugboat/files/db\n    cmds:\n      - task: acquia:fetch-db\n      - task: drupal:import-db\n```\n\nWhen using MySQL as the database engine in DDEV, Tugboat can be configured to\nuse the `percona` Docker image instead of `mysql`:\n```json\n{\n    \"extra\": {\n        \"drainpipe\": {\n            \"tugboat\": {\n                \"percona\": true\n            }\n        }\n    }\n}\n```\n\n### Custom Templates\n\nFor the main `php` service, you can override any build phase template by copying\nit to `.tugboat/drainpipe-templates/`. Example:\n\n```\nmkdir -p .tugboat/drainpipe-templates\ncp vendor/lullabot/drainpipe/scaffold/tugboat/templates/php-init.yml.twig \\\n   .tugboat/drainpipe-templates/\n```\n\nEdit `.tugboat/drainpipe-templates/php-init.yml.twig` to add, remove, or modify\ncommands, then regenerate the Tugboat configuration file with `ddev composer install`.\n\nAvailable Templates:\n\n- `php-init.yml.twig` - Init phase commands\n- `php-update.yml.twig` - Update phase commands\n- `php-build.yml.twig` - Build phase commands\n- `php-online.yml.twig` - Online phase commands\n- `config.yml.twig` - Complete Tugboat configuration (advanced)\n\n### Tasks\n\nTugboat specific tasks are contained in [`tasks/tugboat.yml`](tasks/tugboat.yml).\nAdd the following to your `Taskfile.yml`'s `includes` section to use them:\n```yml\nincludes:\n  tugboat: ./vendor/lullabot/drainpipe/tasks/tugboat.yml\n```\n| Task | Action | Usage |\n|---|---|---|\n| `task tugboat:drush-uli-ready` | Configures Drush with the Tugboat service URL for the environment. | Run it once as part of your Tugboat _build_ or _online_ commands defined at `.tugboat/config.yml`\n\n\nIt is assumed the following tasks exist:\n- `sync`\n- `build`\n- `update`\n- `online`\n\nThe `build`, `sync`, and `update` tasks can be overridden with `sync:tugboat`,\n`build:tugboat`, and `update:tugboat` tasks if required (you will need to re-run\n`composer install` to regenerate the Tugboat scripts if you  are adding this\ntask to your `Taskfile.yml` for the first time).\n\n```\n  sync:\n    desc: \"Fetches a database from Pantheon and imports it\"\n    cmds:\n      - task: pantheon:fetch-db\n      - task: drupal:import-db\n  sync:tugboat:\n    desc: \"Fetches a database from Pantheon and imports it in Tugboat\"\n    cmds:\n      - task: pantheon:fetch-db\n        vars:\n          DB_DIR: /var/lib/tugboat/files/db\n      - task: drupal:import-db\n        vars:\n          DB_DIR: /var/lib/tugboat/files/db\n```\n\n\u003e\u003e\u003e\n💡\n`composer install` should be re-run if any changes are made to the DDEV\nconfiguration.\n\u003e\u003e\u003e\n\n#### Custom init commands\n\nYou can hook into the `init` step of any service by adding them to your\n`Taskfile.yml`. These additional commands will be run at the end of the\ninit phase for the specific Tugboat service.\n\nSupported services:\n\n- Webserver: `tugboat:php:init`\n- Database: `tugboat:mysql:init` / `tugboat:mariadb:init` / `tugboat:postgres:init`\n- Memory cache: `tugboat:redis:init` / `tugboat:memcached:init`\n\nExample:\n\n```\ntugboat:php:init:\n  cmds:\n    - apt-get install -y libldap2-dev\n    - docker-php-ext-install ldap\n```\n\n#### Custom online commands\n\nYou can also add an `online` step to the `php` service by adding a task\nnamed `online:tugboat` and re-running `composer install`.\n\n### Additional Tugboat keys\n\nDrainpipe will fully manage your `.tugboat/config.yml` file, you should not edit\nit. The following keys can be added to your `config.yml` via a\n`.tugboat/config.drainpipe-override.yml` file:\n```\nphp:\n  aliases:\n  urls:\n  screenshot:\n  visualdiff:\nsolr:\n  commands:\n  checkout:\n  depends:\n  volumes:\n  environment:\n  aliases:\n  urls:\n```\n\n### Mail Configuration\n\nBy default, Drainpipe configures Tugboat environments to capture email using Tugboat's built-in mail capture service. This automatically configures:\n- The core mail system to use `php_mail`\n- The Mail System module (if installed) to use `php_mail` as the sender\n- Symfony Mailer (if installed) to send mail through Tugboat's SMTP service\n\nIf you need to use a different mail configuration (e.g., for testing with a specific mail service or module), you can override these settings in your custom settings file (typically `web/sites/default/settings.tugboat.php` or `web/sites/default/settings.php`).\n\nFor example, to use a different mail backend:\n\n```php\n// Override the default Drainpipe mail configuration.\n// This should come after including settings.tugboat.php\nif (getenv('TUGBOAT_REPO')) {\n  // Example: Use a custom mail transport\n  $config['system.mail']['interface']['default'] = 'my_custom_mailer';\n\n  // Example: Configure Symfony Mailer differently\n  $config['symfony_mailer.settings']['default_transport'] = 'custom_transport';\n  $config['symfony_mailer.mailer_transport.custom_transport']['plugin'] = 'smtp';\n  $config['symfony_mailer.mailer_transport.custom_transport']['configuration']['host'] = 'custom.smtp.example.com';\n  $config['symfony_mailer.mailer_transport.custom_transport']['configuration']['port'] = '587';\n}\n```\n\n## Contributor Docs\n\nThis repo is public.\n\nPlease be careful to remove sensitive customer specifics when posting Issues or comments.\n\nFirst time contributors need a maintainers approval for automated tests to run.\n(This is so we aren't at risk of getting a big CI bill accidentally, or maliciously.)\n\nPeer Reviewing by looking at PR code changes is nice.\n\nTesting PR code changes on real sites is extra beneficial.\n\n### Local Development\n\nIn order to test local changes follow the instructions for the [test script](./docs/test-script.md).\n\n### Peer Review Guidelines for Automated Updates\n\nThese are guidelines for conducting peer reviews on automated dependency update pull requests created by Renovate.\n\nAll automated updates submitted by Renovate undergo a series of automated tests via GitHub Actions. These tests are designed to ensure compatibility and stability with the new versions of dependencies.\n\nAll Renovate peer reviews regardless if they're a minor or patch release require:\n1. Reading the change logs carefully to understand the new features and fixes.\n   - Assess if the changes necessitate additional test coverage or could potentially impact existing functionality.\n   - Consider the implications of new features on the project's future development and maintenance.\n2. All tests and checks must pass\n\n#### Handling Version Ranges\n\nSome dependencies allow multiple versions, like `\"drush/drush\": \"^10|^11|^12\"`.\n- Renovate will create pull requests when any of these versions get patch or minor releases.\n- We **DO NOT** want to merge these, because it would pin these packages to a specific version.\n- We **DO** want to allow these pull requests to run checks. This will confirm that the latest version within the range Drainpipe supports is unlikely break builds.\n- After all GitHub Action checks pass, leave a comment on the pull request stating such, close the pull request, and delete the branch.\n\n#### Handling Test Failures\n\nOccasionally, tests may fail due to transient issues or flakiness in the test suite. In such cases:\n\n1. Verify the nature of the test failure to ensure it's not related to the dependency update.\n2. If the failure seems unrelated to the update, re-run the GitHub Actions job to confirm if the issue persists.\n3. Document any recurring flakiness or issues on the pull request then create a new issue linked to the pull request for further investigation.\n\n### Conducting the Peer Review\n\n1. **Review the Automated Update Pull Request (PR)**:\n   - Ensure the PR title and description clearly describe the update and its scope.\n   - Check the list of changed files to understand the extent of the update.\n\n2. **Assess Test Results**:\n   - Ensure all GitHub Actions tests have passed. Pay close attention to tests that touch on updated dependencies.\n   - For failed tests, follow the \"Handling Test Failures\" guidelines above.\n\n3. **Read the Dependency Change Logs**:\n   - For minor point releases, review the dependency's change logs to identify any significant changes or additions.\n   - Evaluate how these changes might affect the Drainpipe project.\n\n5. **Final Decision**:\n   - For patch releases with all tests passing, proceed to merge the update.\n   - For minor point releases, after thorough review and consideration, decide whether to merge the update or request manual testing before merging.\n\n### Releases\n\n#### drainpipe and drainpipe-dev release process\n\nWhen making a release, increase the version based on https://semver.org/\n\n\u003e MAJOR version when you make incompatible API changes\n\u003e MINOR version when you add functionality in a backward compatible manner\n\u003e PATCH version when you make backward compatible bug fixes\n\nSpecifically for drainpipe, when a new \"check\" is added, that might break builds in projects,\nthat would usually be a MINOR release, with a release note about the change.\n\nBefore making a new release, post in the lullabot internal #devops slack channel to coordinate with other maintainers.\n\n1. Generate a GitHub release for drainpipe\n  1. Supply the correct tag based on the changes and semantic versioning standards.\n  2. Use the _Generate release notes_ button and review the changes to confirm the new version is correct based on semantic versioning.\n  3. Set this release as latest and publish.\n2. The release when published will automatically kick off a release of [drainpipe-dev](https://github.com/Lullabot/drainpipe) using the [DrainpipeDev GitHub workflow](https://github.com/Lullabot/drainpipe/actions/workflows/DrainpipeDev.ym).\n3. Visit the [project board](https://github.com/orgs/Lullabot/projects/12/views/1) and archive the _Ready to Release_ column.\n\n#### NPM package release process\n\nTo generate new NPM package releases:\n\n1. Have the latest main branch checked out locally\n2. Run `yarn install \u0026\u0026 yarn lerna publish`\n3. Create a pull request with the changes\n4. Once merged, locally switch to the main branch and run `yarn lerna exec -- npm publish`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flullabot%2Fdrainpipe","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flullabot%2Fdrainpipe","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flullabot%2Fdrainpipe/lists"}