{"id":38113530,"url":"https://github.com/cloud-gov/cf-ex-drupal8","last_synced_at":"2026-01-16T22:06:31.595Z","repository":{"id":37583337,"uuid":"165744298","full_name":"cloud-gov/cf-ex-drupal8","owner":"cloud-gov","description":"Drupal 8 example for cloud.gov/CloudFoundry.","archived":false,"fork":false,"pushed_at":"2024-06-18T19:23:36.000Z","size":3036,"stargazers_count":11,"open_issues_count":18,"forks_count":5,"subscribers_count":10,"default_branch":"main","last_synced_at":"2024-06-19T00:46:53.272Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"SCSS","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cloud-gov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-01-14T22:18:14.000Z","updated_at":"2024-06-18T19:23:37.000Z","dependencies_parsed_at":"2024-04-10T16:41:32.726Z","dependency_job_id":"a81731ce-8e70-436f-86cb-d3e073595a22","html_url":"https://github.com/cloud-gov/cf-ex-drupal8","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cloud-gov/cf-ex-drupal8","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloud-gov%2Fcf-ex-drupal8","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloud-gov%2Fcf-ex-drupal8/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloud-gov%2Fcf-ex-drupal8/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloud-gov%2Fcf-ex-drupal8/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cloud-gov","download_url":"https://codeload.github.com/cloud-gov/cf-ex-drupal8/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloud-gov%2Fcf-ex-drupal8/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28484748,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-16T11:59:17.896Z","status":"ssl_error","status_checked_at":"2026-01-16T11:55:55.838Z","response_time":107,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":"2026-01-16T22:06:30.580Z","updated_at":"2026-01-16T22:06:31.574Z","avatar_url":"https://github.com/cloud-gov.png","language":"SCSS","funding_links":[],"categories":[],"sub_categories":[],"readme":"# cf-ex-drupal8\n\nDrupal 8 example for Cloud Foundry\n\nThis example is archived because it is not being actively maintained. Reach out to the cloud.gov [inquiries@cloud.gov](inquiries@cloud.gov) team if you want more information about deploying drupal to cloud.gov.\n\nWe are planning to have a Drupal 9 example in the coming months.\n\nThis repository demonstrates how to run a production-worthy Drupal 8 site in Cloud Foundry. Folks just getting started with [cloud.gov](https://cloud.gov) can use this as a path from “I have a cloud.gov account” to “I have a production-worthy Drupal site running on a FedRAMP-authorized CSP that I understand how to update, just waiting for me to customize it”.\n\nThe code examples target [cloud.gov](https://cloud.gov) but are easily adapted for any Cloud Foundry target that provides MySQL and object storage (_a la_ AWS's S3).\n\nWe'll also provide some guidance on what someone would need to do to reproduce this on their own codebase if they _don’t_ use this codebase as a starting point.\n\n# Table of contents:\n  * [Deploying Drupal to Cloud Foundry](#deploying-drupal-to-cloud-foundry)\n    + [Install `cf`](#install--cf-)\n    + [Clone a fresh copy of the repo](#clone-a-fresh-copy-of-the-repo)\n    + [Log in and target the appropriate environment](#log-in-and-target-the-appropriate-environment)\n    + [Send our new code to cloud.gov](#send-our-new-code-to-cloudgov)\n  * [Notes on cloud.gov](#notes-on-cloudgov)\n    + [Debugging](#debugging)\n    + [Updating secrets](#updating-secrets)\n  * [Developing and testing changes locally](#developing-and-testing-changes-locally)\n    + [Bring up a local site instance to work with](#bring-up-a-local-site-instance-to-work-with)\n    + [Making styling changes](#making-styling-changes)\n    + [Helpful scripts](#helpful-scripts)\n    + [Using the S3 storage from another environment locally](#using-the-s3-storage-from-another-environment-locally)\n    + [Making configuration changes the DevOps way](#making-configuration-changes-the-devops-way)\n    + [Making content changes the DevOps way](#making-content-changes-the-devops-way)\n    + [Removing dependencies](#removing-dependencies)\n    + [Upgrading dependencies (e.g. Drupal)](#upgrading-dependencies--eg-drupal-)\n    + [Common errors](#common-errors)\n      - [Edits to `web/sites/default/xxx` won't go away](#edits-to--web-sites-default-xxx--won-t-go-away)\n    + [Start from scratch](#start-from-scratch)\n  \n## Deploying Drupal to Cloud Foundry\n\nWe prefer deploying code through a continuous integration system. This ensures\nreproducibility and allows us to add additional safeguards. Regardless of\nenvironment, however, our steps for deploying code are more or less the same:\n1. Install the `cf` executable (this can be done once)\n1. Clone a *fresh* copy of the repository (this must be done every time)\n1. Log into cloud.gov and target the appropriate environment\n1. Send our new code to cloud.gov for deployment\n\n### Install `cf`\n\nFollow the Cloud Foundry\n[instructions](https://docs.cloudfoundry.org/cf-cli/install-go-cli.html) for\ninstalling the `cf` executable. This command-line interface is our primary\nmechanism for interacting with cloud.gov.\n\nIf performing a deployment manually (outside of CI), note that you'll only\nneed to install this executable once for use with all future deployments.\n\n### Clone a fresh copy of the repo\n\nIn a continuous integration environment, we'll always check out a fresh copy\nof the code base, but if deploying manually, it's import to make a new, clean\ncheckout of our repository to ensure we're not sending up additional files.\nNotably, using `git status` to check for a clean environment is _not_ enough;\nour `.gitignore` does not match the `.cfignore` so git's status output isn't a\nguaranty that there are no additional files. If deploying manually, it makes\nsense to create a new directory and perform the checkout within that\ndirectory, to prevent conflicts with our local checkout.\n\n```shell\ngit clone https://github.com/18F/cf-ex-drupal8.git\n```\n\nAs we don't need the full repository history, we could instead use an\noptimized version of that checkout:\n\n```shell\ngit clone https://github.com/18F/cf-ex-drupal8.git --depth=1\n```\n\nWe'll also want to **c**hange our **d**irectory to be inside the repository.\n\n```shell\ncd cf-ex-drupal8\n```\n\n### Log in and target the appropriate environment\nNow we need to make sure we're logged into Cloud Foundry...\n\n```shell\ncf login -a api.fr.cloud.gov --sso\n```\n\nAnd check that we're pointing at the desired organization and space:\n\n```shell\ncf target\n```\n\nIf you want to change the target organization and space, you can provide parameters to `target`:\n\n```shell\ncf target -o \u003cORGNAME\u003e -s \u003cSPACENAME\u003e\n```\n\n### Send our new code to cloud.gov\n\nWe've included a simple script that deploys Drupal the first time.\n\n```shell\n./deploy-cloudgov.sh\n```\n\nThe script creates the services you need (if they are not already created),\nwaits until the services are up, and then launches the app and tells you\nwhat URL you should go to.\n\nBy default, it will use a small cloud.gov database, which is ideal for testing\nbut not a production environment.\n\nIf you wish to do a deploy for a production environment and get a larger database\nwith redundancy, then you should run:\n\n```shell\n./deploy-cloudgov.sh prod\n```\n\nAs a part of this process, some secrets are generated, like the initial\nroot password.  If you want, you can override this by saying:\n`export ROOT_USER_PASS=yourReallyGr3atPassw0rd.` before running the\n`deploy-cloudgov.sh` script.\n\n## Notes on cloud.gov\n\nOur preferred platform-as-a-service is [cloud.gov](https://cloud.gov/), due to\nits\n[FedRAMP-Authorization](https://cloud.gov/overview/security/fedramp-tracker/).\ncloud.gov uses the open source [Cloud Foundry](https://www.cloudfoundry.org/)\nplatform, which is very similar to [Heroku](https://www.heroku.com/). See\ncloud.gov's excellent [user docs](https://cloud.gov/docs/) to get acquainted\nwith the system.\n\n### Debugging\n\nWe'll assume you're already logged into cloud.gov. From there,\n\n```shell\ncf apps\n```\n\nwill give a broad overview of the current application instances. We expect two\n\"web\" instances and one \"cronish\" worker in our environments, as described in\n[our manifest file](manifest.yml).\n\n```shell\ncf app web\n```\n\nwill give us more detail about the \"web\" instances, specifically CPU, disk,\nand memory usage.\n\n```shell\ncf logs web\n```\n\nwill let us attach to the emitted apache logs of our running \"web\" instances.\nIf we add the `--recent` flag, we'll instead get output from our *recent* log\nhistory (and not see new logs as they come in). We can use these logs to debug\n500 errors. Be sure to look at cloud.gov's [logging\ndocs](https://cloud.gov/docs/apps/logs/) (particularly, how to use Kibana) for\nmore control.\n\nIf necessary, we can also `ssh` into running instances. This should generally\nbe avoided, however, as all modifications will be lost on next deploy. See the\ncloud.gov [docs on the topic](https://cloud.gov/docs/apps/using-ssh/) for more\ndetail -- be sure to read the step about setting up the ssh environment.\n\n```shell\ncf ssh web\n```\n\nWhile the database isn't generally accessible outside the app's network, we\ncan access it by setting up an SSH tunnel, as described in the\n[cf-service-connect](https://github.com/18F/cf-service-connect#readme) plugin.\nNote that the `web` and `cronish` instances don't have a `mysql` client (aside\nfrom PHP's PDO); sshing into them likely won't help.\n\nOf course, there are many more useful commands. Explore the cloud.gov [user\ndocs](https://cloud.gov/docs/) to learn about more.\n\n### Updating secrets\n\nAs our secrets are stored in a cloud.gov \"user-provided service\", to add new\nones (or rotate existing secrets), we'll need to call the\n`update-user-provided-service` command. It can't be updated incrementally,\nhowever, so we'll need to set all of the secrets (including those that remain\nthe same) at once.\n\nTo grab the previous versions of these values, we can run\n\n```shell\ncf env web\n```\n\nand look in the results for the credentials of our \"secrets\" service (it'll be\npart of the `VCAP_SERVICES` section). Then, we update our `secrets` service\nlike so:\n\n```shell\ncf update-user-provided-service secrets -p '{\"SAMPLE_ACCOUNT\":\"Some Value\", \"SAMPLE_CLIENT\":\"Another value\", ...}'\n```\n\n## Developing and testing changes locally\n\n### Bring up a local site instance to work with\nWe'll use [Git](https://git-scm.com/) to pull down and manage our codebase.\nThere are [many](https://guides.github.com/introduction/git-handbook/)\n[excellent](https://git-scm.com/book/en/v2/Getting-Started-Git-Basics)\n[tutorials](http://git.huit.harvard.edu/guide/) for getting started with git,\nso we'll defer to them here. We'll assume you have cloned our repository and\nare now within it:\n\n```shell\ngit clone https://github.com/18F/cf-ex-drupal8.git\ncd cf-ex-drupal8\n```\n\nWe use [Docker](https://www.docker.com/) to get a local environment running\nquickly.\n[Download](https://store.docker.com/search?type=edition\u0026offering=community)\nand install the runtime compatible with your system. Note that [Docker for\nWindows](https://www.docker.com/docker-windows) requires Windows 10; use\n[Docker Toolbox](https://docs.docker.com/toolbox/toolbox_install_windows/) on\nolder Windows environments. Docker will manage our PHP dependencies, get\nApache running, and generally allow us to run an instance of our application\nlocally. We'll be using the\n[bash](https://www.gnu.org/software/bash/)-friendly scripts in `bin`, but they\nwouldn't need to be modified substantially for Windows or other environments.\n\nOur first step is to run\n\n```shell\nbin/composer install\n```\n\nThis command will start by building a Docker image with the PHP modules we\nneed, unless the image already exists. It will then use\n[Composer](https://getcomposer.org/) to install dependencies from our\n`composer.lock` file. We can ignore the warning about running as root, as the\n\"root\" in question is the root user _within_ the container. Should we need to\nadd dependencies in the future, we can use `bin/composer require` as described\nin Composer's [docs](https://getcomposer.org/doc/03-cli.md#require).\n\nNext, we can start our application:\n\n```shell\ndocker-compose up\n```\n\nThis will start up the database (MySQL) and then run our bootstrap script to\ninstall Drupal. The initial installation and configuration import will take\nseveral minutes, but we should see status updates in the terminal.\n\nAfter we see a message about `apache2 -D FOREGROUND`, we're good to go.\nNavigate to [http://localhost:8080/](http://localhost:8080) and log in as the\nroot user (username and password are both \"root\").\n\nTo stop the service, press `ctrl-c` in the terminal. The next time we start\nit, we'll see a similar bootstrap process, but it should be significantly\nfaster.\n\nAs the service runs, we can directly modify the PHP files in our app and see\nour changes in near-real time.\n\n### Making styling changes\n\nThis codebase's theme is a subtheme of the [U.S. Web Design System](https://drupal.org/project/uswds) theme. Accordingly, its overrides are stored in `/web/themes/custom/your_uswds_subtheme`.\n\nOur style changes are all within the context of the `your_uswds_subtheme` \"theme\", so we'll\nstart by getting there:\n\n```shell\ncd web/themes/custom/your_uswds_subtheme\n```\n\nIf this is the first time we're editing a theme, we next need to install all\nof the relevant node modules:\n\n```shell\nbin/npm install\n```\n\nFinally, we'll start our \"watch\" script:\n\n```shell\nbin/npm run build:watch\n```\n\nAs long as that command is running, it'll watch every `.scss` file in the `sass/` folder for changes, compiling and saving CSS in the `assets/css/` folder every time you save a change to a `.scss` file.\n\nNow, in a separate Terminal window and/or your favorite text editor, you can make changes to `web/themes/custom/your_uswds_subtheme/sass/uswds.scss` (or `_variables.scss`) and have your changes saved.\n\n### Helpful scripts\n\nWithin the `bin` directory, there are a handful of helpful scripts to make\nrunning `drupal`, `drush`, etc. within the context of our Dockerized app\neasier. As noted above, they are written with bash in mind, but should be easy\nto port to other environments.\n\n### Using the S3 storage from another environment locally\n\nCurrently, even when running locally we need to simulate the S3\nenvironment by adding its credentials to the `VCAP_SERVICES`\nenvironment variable.\n\nTo find the values we're using in cloud.gov, use\n\n```shell\ncf env web\n```\n\nThen edit `docker-compose.yml` and insert\nsomething similar to the following above \"user-provided\":\n\n```json\n\"s3\": [{\n  \"name\": \"storage\",\n  \"credentials\": {\n   \"access_key_id\": \"SECRET\",\n   \"bucket\": \"SECRET\",\n   \"region\": \"SECRET\",\n   \"secret_access_key\": \"SECRET\"\n  }\n}],\n```\n\nAs with other edits to the local secrets, extra care should be taken when\nexporting your config, lest those configuration files contain the true secret\nvalues rather than dummy \"SECRET\" strings.\n\n### Making configuration changes the DevOps way\n\nMaking configuration changes to the application comes in roughly eight small steps:\n1. get the latest code\n1. create a feature branch\n1. make any dependency changes\n1. edit the Drupal admin\n1. export the configuration\n1. commit the changes\n1. push your branch to GitHub\n1. create a pull request to be reviewed\n\nTo get the latest code, we can `fetch` it from GitHub.\n\n```shell\ngit fetch origin\ngit checkout origin/master\n```\n\nAlternatively:\n\n```shell\ngit checkout master\ngit pull origin master\n```\n\nWe then create a \"feature\" branch, meaning a branch of development that's\nfocused on adding a single feature. We'll need to name the branch something\nunique, likely related to the task we're working on (perhaps including an\nissue number, for example).\n\n```shell\ngit checkout -b 333-add-the-whatsit\n```\n\nIf we are installing a new module or otherwise updating our dependencies, we\nnext use composer. For example:\n\n```shell\nbin/composer require drupal/some-new-module\n```\n\nSee the \"Removing dependencies\" section below for notes on that topic; it's a\nbit different than installation/updates.\n\nIf we're making admin changes (including enabling any newly installed\nmodules), we'll need to start our app locally.\n\n```sh\ndocker-compose down # stop any running instance\ndocker-compose up # start a new one with our code\n```\n\nThen navigate to [http://localhost:8080](http://localhost:8080) and log in as\nthe root/root. Modify whatever settings desired, which will modify them in\nyour local database. We'll next need to export those configurations to the\nfile system:\n\n```shell\nbin/drupal config:export\n```\n\nWe're almost done! We next need to review all of the changes and commit those\nthat are relevant. Your git tool will have a diff viewer, but if you're using\nthe command line, try\n\n```shell\ngit add -p\n```\n\nto interactively select changes to stage for the commit. Once the changes are\nstaged, commit them, e.g. with\n\n```shell\ngit commit -v\n```\n\nBe sure to add a descriptive commit message. Now we can send the changes to\nGitHub:\n\n```shell\ngit push origin 333-add-the-whatsit\n```\n\nAnd request a review in GitHub's interface.\n\n### Making content changes the DevOps way\n\nWe'll also treat some pieces of content similar to configuration -- we want to\ndeploy it with the code base rather than add/modify it in individual\nenvironments. The steps for this are very similar to the workflow for configuration:\n\n1. get the latest code\n1. create a feature branch\n1. add/edit content in the Drupal admin\n1. export the content\n1. commit the changes\n1. push your branch to GitHub\n1. create a pull request to be reviewed\n\nThe first two steps are identical to the Config workflow, so we'll skip to the\nthird. Start the application:\n\n```shell\ndocker-compose up\n```\n\nThen [log in](http://localhost:8080/user/login) as root (password: root).\nCreate or edit content (e.g. Aggregator feeds, pages, etc.) through the Drupal\nadmin.\n\nNext, we'll export this content via Drush:\n\n```sh\n# Export all entities of a particular type\nbin/drush default-content-deploy:export [type-of-entity e.g. aggregator_feed]\n# Export individual entities\nbin/drush default-content-deploy:export [type-of-entity] --entity-id=[ids e.g. 1,3,7]\n```\n\nThen, we'll review all of the changes and commit those that are relevant.\nNotably, we're expecting new or modified files in `web/sites/default/content`.\nAfter committing, we'll sent to GitHub and create a pull request as with\nconfig changes.\n\n### Removing dependencies\n\nAs we add modules to our site, they're rolled out via configuration\nsynchronization. This'll run the installation of new modules, including\nsetting up database tables. Unfortunately, removing modules isn't as simple as\ndeleting the PHP lib and deactivating the plugin. Modules and themes need to\nbe fully uninstalled, which will remove their content from the database and\nperform other sorts of cleanup. Unfortunately, to do that, we need to have the\nPHP lib around to run the cleanup.\n\nOur solution is to have a step in our bootstrap script which uninstalls\nmodules/themes prior to configuration import. To do this, we'll need to keep\nthe PHP libs around so that the uninstallation hooks can be called. After\nwe're confident that the library is uninstalled in all our environments, we\ncan also remove it from the composer dependencies.\n\nSee the `module:uninstall` and `theme:uninstall` steps of the bootstrap script\nto see how this is implemented.\n\n### Upgrading dependencies (e.g. Drupal)\n\nUpdating dependencies through Composer is simple, though somewhat slow. First,\nwe should spin down our local install:\n\n```shell\ndocker-compose down\n```\n\nThen, we run the\n[`update`](https://getcomposer.org/doc/01-basic-usage.md#updating-dependencies-to-their-latest-versions)\ncommand:\n\n```shell\nbin/composer update [name-of-package, e.g. drupal/core]\n```\n\nAfter crunching away a while, you should see (e.g. via `git status`) that the\n`composer.lock` file has changed. Note that this command *doesn't* modify\n`composer.json` -- it will only update the package in a way that's\n[compatible](https://semver.org/). If you need to upgrade a major version\n(i.e. a backward-incompatible release), use the\n[`require`](https://getcomposer.org/doc/03-cli.md#require) command, e.g.\n\n```shell\nbin/composer require drupal/core:9.*\n```\n\nAfter installing the update, we should spin up our local instance\n\n```shell\ndocker-compose up\n```\n\nand browse around [http://localhost:8080/](http://localhost:8080/) to make\nsure nothing's obviously broken. We shouldn't expect to see anything amiss if\nwe've just `update`d, but need to be more careful around major version\nchanges.\n\nWe should then proceed with steps five through eight (exporting the config,\ncommitting, sending to GitHub, etc.). Even though we haven't actively modified\nany of the configurations, the updated libraries may have generated new ones\nwhich would be good to capture.\n\n### Common errors\n\n#### Edits to `web/sites/default/xxx` won't go away\nDrupal's installation changes the directory permissions for\n`web/sites/default`, which can prevent git from modifying these files. As\nwe're working locally, those permissions restrictions aren't incredibly\nimportant. We can revert them by granting ourselves \"write\" access again. In\nunix environments, we can run\n\n```shell\nchmod u+w web/sites/default\n```\n\n### Start from scratch\n\nAs Docker is managing our environment, it's relatively easy to blow away our\ndatabase and start from scratch.\n\n```shell\ndocker-compose down -v\n```\n\nGenerally, `down` spins down the running environment but doesn't delete any\ndata. The `-v` flag, however, tells Docker to delete our data \"volumes\",\nclearing away all the database files.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloud-gov%2Fcf-ex-drupal8","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcloud-gov%2Fcf-ex-drupal8","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloud-gov%2Fcf-ex-drupal8/lists"}