├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── box.json ├── composer.json ├── phpunit.xml ├── platform-docker ├── platform-docker.phar ├── resources ├── conf │ ├── mysql.cnf │ ├── nginx.conf │ ├── php │ │ ├── 5.6 │ │ │ ├── fpm.conf │ │ │ └── php.ini │ │ ├── 7.0 │ │ │ ├── fpm.conf │ │ │ └── php.ini │ │ └── 7.1 │ │ │ ├── fpm.conf │ │ │ └── php.ini │ └── solr │ │ ├── 4.x │ │ ├── README.md │ │ ├── elevate.xml │ │ ├── mapping-ISOLatin1Accent.txt │ │ ├── protwords.txt │ │ ├── schema.xml │ │ ├── schema_extra_fields.xml │ │ ├── schema_extra_types.xml │ │ ├── solrconfig.xml │ │ ├── solrconfig_extra.xml │ │ ├── solrcore.properties │ │ ├── stopwords.txt │ │ └── synonyms.txt │ │ └── 6.x │ │ ├── elevate.xml │ │ ├── mapping-ISOLatin1Accent.txt │ │ ├── protwords.txt │ │ ├── schema.xml │ │ ├── schema_extra_fields.xml │ │ ├── schema_extra_types.xml │ │ ├── schema_legacy_fields.xml │ │ ├── schema_legacy_types.xml │ │ ├── solrconfig.xml │ │ ├── solrconfig_spellcheck.xml │ │ ├── solrcore.properties │ │ ├── stopwords.txt │ │ └── synonyms.txt ├── drupal-enable-profiling.patch ├── images │ └── php │ │ ├── 5.6 │ │ └── Dockerfile │ │ ├── 7.0 │ │ └── Dockerfile │ │ └── 7.1 │ │ └── Dockerfile ├── ssl │ ├── nginx.crt │ └── nginx.key ├── stacks │ ├── drupal7 │ │ ├── drushrc.php │ │ ├── settings.local.php │ │ └── settings.php │ ├── drupal8 │ │ ├── drushrc.php │ │ ├── services.yml │ │ ├── settings.local.php │ │ └── settings.php │ └── wordpress │ │ ├── wp-config.local.php │ │ └── wp-config.php └── wordpress-enable-profiling.patch ├── src ├── Application.php ├── BrowserTrait.php ├── Command │ ├── Command.php │ ├── Docker │ │ ├── DockerCommand.php │ │ ├── LogsCommand.php │ │ ├── ProxyCommand.php │ │ ├── RebuildCommand.php │ │ ├── RestartCommand.php │ │ ├── SshCommand.php │ │ ├── StopCommand.php │ │ └── UpCommand.php │ ├── DrushCommand.php │ ├── Flamegraph │ │ ├── CreateCommand.php │ │ ├── SetupCommand.php │ │ └── UnpatchCommand.php │ ├── InitCommand.php │ ├── LinkCommand.php │ ├── ListCommand.php │ ├── Project │ │ ├── BehatCommand.php │ │ └── DbSyncCommand.php │ └── Providers │ │ ├── Commerce2xProviderCommand.php │ │ ├── PlatformshProviderCommand.php │ │ └── ProviderCommand.php ├── Config.php ├── Descriptor │ └── CustomTextDescriptor.php ├── Docker │ ├── ComposeConfig.php │ └── ComposeContainers.php ├── Platform.php ├── PlatformAppConfig.php ├── PlatformServiceConfig.php ├── Stacks │ ├── Drupal.php │ ├── StackTypeInterface.php │ ├── StacksBase.php │ ├── StacksFactory.php │ └── WordPress.php └── YamlConfigReader.php ├── stub.php └── tests ├── Utils ├── BaseUtilsTest.php ├── ComposeContainersTest.php ├── ConfigTest.php ├── PlatformAppConfigTest.php ├── PlatformServiceConfigTest.php └── PlatformTest.php ├── bootstrap.php └── fixtures ├── .platform.app.yaml ├── 5.6.platform.app.yaml ├── services.yaml └── solr-3-services.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .idea 3 | composer.lock 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 5.4 4 | - 5.5 5 | - 5.6 6 | - hhvm 7 | - 7.0 8 | matrix: 9 | allow_failures: 10 | - php: 7.0 11 | fast_finish: true 12 | sudo: false 13 | cache: 14 | directories: 15 | - $HOME/.composer/cache 16 | install: 17 | - composer install --no-interaction 18 | - travis_wait composer update --prefer-lowest --no-interaction 19 | script: 20 | - ./vendor/bin/phpunit -c ./phpunit.xml --coverage-text 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | {description} 294 | Copyright (C) {year} {fullname} 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | {signature of Ty Coon}, 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | 341 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # platform-docker [![Build Status](https://travis-ci.org/mglaman/platform-docker.svg?branch=master)](https://travis-ci.org/mglaman/platform-docker) 2 | 3 | **Platform Docker** is a CLI tool for scaffolding docker-compose configuration for PHP projects, currently opinionated at PHP projects. Running ````platform-docker```` 4 | in a directory will create a multi-container application environment for local development. 5 | 6 | Currently support is focused at Drupal 7 and Drupal 8. However there is rudimentary WordPress support. Generic PHP applications should be easy to implement. 7 | 8 | ## Requirements 9 | 10 | * [Composer](https://getcomposer.org/) 11 | * [Docker](https://www.docker.com/) 12 | * [Docker Compose](https://docs.docker.com/compose/) 13 | 14 | ## Installation 15 | 16 | First, if you do not have Docker then head over to their [documentation](https://docs.docker.com/) and see how to install for your machine. 17 | For OSX and Windows users, make sure you have [Virtual Box](https://www.virtualbox.org/wiki/Downloads) installed and docker-machine configured, follow [these instructions](https://docs.docker.com/machine/get-started/#/create-a-machine) for the later and ensure you have 18 | ran `docker-machine create --driver virtualbox default`. 19 | 20 | ```` 21 | composer global require mglaman/platform-docker 22 | ```` 23 | 24 | ## Usage 25 | 26 | Use within any directory. Until the app itself can scaffold a folder, it's expecting a folder structure of 27 | 28 | * /shared (if not present it will be made) 29 | * /www (required, this is your build) 30 | * /repository (not required, but opinionated this is the source of what was built.) 31 | * /tests (default directory it will look for Behat tests, however checks shared and www) 32 | 33 | If you are on Mac OS X, export ````PLATFORM_DOCKER_MACHINE_NAME```` with your Docker machine name. The tool will automatically boot the machine or export its environment information as needed. For example, put ````12 export PLATFORM_DOCKER_MACHINE_NAME="vmname"```` in your .bash_profile. 34 | 35 | ### Features 36 | 37 | #### Redis 38 | There is a redis container available. Currently it can be added by adding the following to .platform-project in the root directory of the project 39 | 40 | ```` 41 | services: 42 | - redis 43 | ````` 44 | 45 | #### Solr 46 | An Apache Solr container is available with the default server URI is ````http://solr:8983/solr```` Currently it can be added by adding the following to .platform-project in the root directory of the project 47 | 48 | ```` 49 | services: 50 | - solr 51 | ````` 52 | 53 | #### Flamegraphs 54 | There is a helper command which patches Drupal to log xhprof items, and then turn them into a flamegraph. 55 | 56 | #### Behat tests 57 | Searches for behat.yml files, laucnches a Selenium (Firefox) container and executes tests. 58 | 59 | ### Commands 60 | 61 | ```` 62 | Available commands: 63 | drush Runs a Drush command for environment. 64 | help Displays help for a command 65 | init Setup Platform and Docker Compose files 66 | link Displays link to local environment, with port. 67 | list Lists commands 68 | docker 69 | docker:logs Tails the logs of a specific service container 70 | docker:proxy (proxy) Starts the nginx proxy container 71 | docker:rebuild Rebuild configurations and containers 72 | docker:restart (reboot) Restarts the docker containers 73 | docker:ssh Allows for quick SSH into a service container. 74 | docker:stop (stop) Stops the docker containers 75 | docker:up (start) Starts the docker containers 76 | flamegraph 77 | flamegraph:create Creates a flamegraph from xhprof folder contents. 78 | flamegraph:setup Sets the project up for generating flamegrapghs. 79 | flamegraph:unpatch Unpatches index.php to stop xhprof logging. 80 | project 81 | project:behat (behat) Runs behat test suite for project. Checks ./tests, ./www, ./shared and ./repository by default. 82 | project:db-sync Syncs database from environment to local 83 | provider 84 | provider:platformsh (platformsh) Sets up a Platform.sh project 85 | ```` 86 | -------------------------------------------------------------------------------- /box.json: -------------------------------------------------------------------------------- 1 | { 2 | "chmod": "0755", 3 | "directories": [ 4 | "resources", 5 | "src" 6 | ], 7 | "files": [ 8 | "vendor/autoload.php", 9 | "vendor/symfony/console/Resources/bin/hiddeninput.exe" 10 | ], 11 | "finder": [ 12 | { 13 | "in": "vendor", 14 | "name": "*.php", 15 | "exclude": [ 16 | "phpunit", 17 | "Tests", 18 | "tests" 19 | ] 20 | } 21 | ], 22 | "main": "platform-docker", 23 | "output": "platform-docker.phar", 24 | "stub": "stub.php" 25 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mglaman/platform-docker", 3 | "description": "An opinionated platform stack for Drupal with Docker Compose", 4 | "license": "GPL-2.0+", 5 | "minimum-stability": "stable", 6 | "require": { 7 | "symfony/config": "^2.7|^3.0", 8 | "symfony/console": "^2.7", 9 | "symfony/filesystem": "^2.7|^3.0", 10 | "symfony/yaml": "^2.7|^3.0", 11 | "symfony/process": "^2.7|^3.0", 12 | "mglaman/toolstack-helper": "^0", 13 | "mglaman/docker-helper": "^0" 14 | }, 15 | "require-dev": { 16 | "phpunit/phpunit": "^4.3" 17 | }, 18 | "suggest": { 19 | "drush/drush": "For Drupal projects", 20 | "wp-cli/wp-cli": "For WordPress projects", 21 | "platformsh/cli": "For Platform.sh projects" 22 | }, 23 | "autoload": { 24 | "psr-4": { 25 | "mglaman\\PlatformDocker\\": "src" 26 | } 27 | }, 28 | "autoload-dev": { 29 | "psr-4": { 30 | "mglaman\\PlatformDocker\\Tests\\": "tests" 31 | } 32 | }, 33 | "authors": [ 34 | { 35 | "name": "Matt Glaman", 36 | "email": "nmd.matt@gmail.com" 37 | } 38 | ], 39 | "bin": [ 40 | "platform-docker" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./tests/ 6 | 7 | 8 | 9 | 10 | ./src/ 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /platform-docker: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | run($input); 22 | -------------------------------------------------------------------------------- /platform-docker.phar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mglaman/platform-docker/217be131f70454a0f2c62a7ae0553c432731bfdf/platform-docker.phar -------------------------------------------------------------------------------- /resources/conf/mysql.cnf: -------------------------------------------------------------------------------- 1 | [client] 2 | port = 3306 3 | socket = /var/run/mysqld/mysqld.sock 4 | 5 | [mysqld_safe] 6 | pid-file = /var/run/mysqld/mysqld.pid 7 | socket = /var/run/mysqld/mysqld.sock 8 | nice = 0 9 | 10 | [mysqld] 11 | # Make no assumptions about what user is running mysqld since docker can mess with this. This caused problems on Ubuntu 12 | # 16.04 and Mint 17 13 | # user = root 14 | pid-file = /var/run/mysqld/mysqld.pid 15 | socket = /var/run/mysqld/mysqld.sock 16 | port = 3306 17 | basedir = /usr 18 | datadir = /var/lib/mysql 19 | tmpdir = /tmp 20 | lc-messages-dir = /usr/share/mysql 21 | explicit_defaults_for_timestamp 22 | 23 | # Instead of skip-networking the default is now to listen only on 24 | # localhost which is more compatible and is not less secure. 25 | bind-address = 0.0.0.0 26 | 27 | log-error = /var/log/mysql/error.log 28 | 29 | # Recommended in standard MySQL setup 30 | sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES 31 | 32 | # Disabling symbolic-links is recommended to prevent assorted security risks 33 | symbolic-links=0 34 | 35 | # Drupal DB tweaks - https://groups.drupal.org/node/289613 36 | key_buffer_size = 32M 37 | max_allowed_packet = 16M 38 | tmp_table_size = 64M 39 | max_heap_table_size = 64M 40 | query_cache_size = 4M 41 | query_cache_limit = 256K 42 | thread_cache_size = 128 43 | 44 | # If you do not need full ACID compliance, this can be set 0. It means that you will loose data for 45 | # the last 1-2 seconds but makes the system much faster. 46 | innodb_flush_log_at_trx_commit=0 47 | # Various performance settings. See documentation for more information. 48 | innodb_log_buffer_size=4M 49 | innodb_thread_concurrency=8 50 | innodb_flush_method=O_DIRECT 51 | 52 | # * IMPORTANT: Additional settings that can override those from this file! 53 | # The files must end with '.cnf', otherwise they'll be ignored. 54 | # 55 | !includedir /etc/mysql/conf.d/ 56 | -------------------------------------------------------------------------------- /resources/conf/nginx.conf: -------------------------------------------------------------------------------- 1 | # http://wiki.nginx.org/Symfony 2 | # http://symfony.com/doc/current/cookbook/configuration/web_server_configuration.html 3 | 4 | upstream phpfcgi { 5 | server phpfpm:9000; 6 | # server unix:/var/run/php5-fpm.sock; #for PHP-FPM running on UNIX socket 7 | } 8 | 9 | server { 10 | listen 80 default_server; 11 | listen [::]:80 default_server ipv6only=on; 12 | listen 443 ssl; 13 | 14 | server_name {{ platform }}; 15 | ssl_certificate /etc/nginx/ssl/nginx.crt; 16 | ssl_certificate_key /etc/nginx/ssl/nginx.key; 17 | 18 | root /var/platform/{{ docroot }}; 19 | 20 | # strip index.php/ prefix if it is present 21 | rewrite ^/index\.php/?(.*)$ /$1 permanent; 22 | 23 | location / { 24 | sendfile off; 25 | index index.php; 26 | try_files $uri @rewriteapp; 27 | } 28 | 29 | location @rewriteapp { 30 | rewrite ^(.*)$ /index.php/$1 last; 31 | } 32 | 33 | # pass the PHP scripts to FastCGI server from upstream phpfcgi 34 | location ~ \.php(/|$) { 35 | fastcgi_pass phpfcgi; 36 | fastcgi_split_path_info ^(.+\.php)(/.*)$; 37 | include fastcgi_params; 38 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 39 | fastcgi_param HTTPS off; 40 | # Allow access to /app_dev.php & /config.php 41 | fastcgi_param REMOTE_ADDR 127.0.0.1; 42 | # Server variable to alert we're in docker 43 | fastcgi_param PLATFORM_DOCKER "PLATFORM_DOCKER"; 44 | 45 | # Need to make this dynamic somehow. 46 | fastcgi_param PHP_IDE_CONFIG "serverName={{ platform }}"; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /resources/conf/php/5.6/fpm.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | error_log = /proc/self/fd/2 3 | daemonize = no 4 | 5 | [www] 6 | user = root 7 | group = root 8 | php_admin_value[date.timezone] = 'America/New_York' 9 | php_admin_flag[short_open_tag] = off 10 | 11 | access.log = /proc/self/fd/2 12 | listen = [::]:9000 13 | pm = dynamic 14 | pm.max_children = 100 15 | pm.start_servers = 30 16 | pm.min_spare_servers = 10 17 | pm.max_spare_servers = 50 18 | catch_workers_output = yes 19 | -------------------------------------------------------------------------------- /resources/conf/php/5.6/php.ini: -------------------------------------------------------------------------------- 1 | memory_limit=512M 2 | date.timezone=America/Chicago 3 | always_populate_raw_post_data=-1 4 | [xdebug] 5 | zend_extension=xdebug.so 6 | xdebug.max_nesting_level=256 7 | xdebug.remote_enable=1 8 | xdebug.remote_autostart=0 9 | xdebug.remote_connect_back=0 10 | xdebug.remote_host=172.17.42.1 11 | xdebug.remote_port=9000 12 | xdebug.remote_log=/tmp/php5-xdebug.log 13 | 14 | [xhprof] 15 | extension=xhprof.so 16 | xhprof.output_dir=/var/platform/xhprof 17 | 18 | [opcache] 19 | opcache.enable=1 20 | opcache.memory_consumption=192 21 | opcache.interned_strings_buffer=16 22 | opcache.max_accelerated_files=4000 23 | opcache.fast_shutdown=1 24 | opcache.validate_timestamps=1 25 | opcache.revalidate_freq=0 26 | 27 | [memcache] 28 | extension=memcache.so 29 | 30 | [redis] 31 | extension=redis.so 32 | -------------------------------------------------------------------------------- /resources/conf/php/7.0/fpm.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | error_log = /proc/self/fd/2 3 | daemonize = no 4 | 5 | [www] 6 | user = root 7 | group = root 8 | php_admin_value[date.timezone] = 'America/New_York' 9 | php_admin_flag[short_open_tag] = off 10 | 11 | access.log = /proc/self/fd/2 12 | listen = [::]:9000 13 | pm = dynamic 14 | pm.max_children = 100 15 | pm.start_servers = 30 16 | pm.min_spare_servers = 10 17 | pm.max_spare_servers = 50 18 | catch_workers_output = yes 19 | -------------------------------------------------------------------------------- /resources/conf/php/7.0/php.ini: -------------------------------------------------------------------------------- 1 | memory_limit=512M 2 | date.timezone=America/Chicago 3 | always_populate_raw_post_data=-1 4 | 5 | [xdebug] 6 | xdebug.max_nesting_level=256 7 | xdebug.remote_enable=1 8 | xdebug.remote_autostart=0 9 | xdebug.remote_connect_back=0 10 | xdebug.remote_host=172.17.42.1 11 | xdebug.remote_port=9000 12 | xdebug.remote_log=/tmp/php5-xdebug.log 13 | 14 | [opcache] 15 | opcache.enable=1 16 | opcache.memory_consumption=192 17 | opcache.interned_strings_buffer=16 18 | opcache.max_accelerated_files=4000 19 | opcache.fast_shutdown=1 20 | opcache.validate_timestamps=1 21 | opcache.revalidate_freq=0 22 | 23 | [memcache] 24 | #extension=memcache.so 25 | -------------------------------------------------------------------------------- /resources/conf/php/7.1/fpm.conf: -------------------------------------------------------------------------------- 1 | [global] 2 | error_log = /proc/self/fd/2 3 | daemonize = no 4 | 5 | [www] 6 | user = root 7 | group = root 8 | php_admin_value[date.timezone] = 'America/New_York' 9 | php_admin_flag[short_open_tag] = off 10 | 11 | access.log = /proc/self/fd/2 12 | listen = [::]:9000 13 | pm = dynamic 14 | pm.max_children = 100 15 | pm.start_servers = 30 16 | pm.min_spare_servers = 10 17 | pm.max_spare_servers = 50 18 | catch_workers_output = yes 19 | -------------------------------------------------------------------------------- /resources/conf/php/7.1/php.ini: -------------------------------------------------------------------------------- 1 | memory_limit=512M 2 | date.timezone=America/Chicago 3 | always_populate_raw_post_data=-1 4 | 5 | [xdebug] 6 | xdebug.max_nesting_level=256 7 | xdebug.remote_enable=1 8 | xdebug.remote_autostart=0 9 | xdebug.remote_connect_back=0 10 | xdebug.remote_host=172.17.42.1 11 | xdebug.remote_port=9000 12 | xdebug.remote_log=/tmp/php5-xdebug.log 13 | 14 | [opcache] 15 | opcache.enable=1 16 | opcache.memory_consumption=192 17 | opcache.interned_strings_buffer=16 18 | opcache.max_accelerated_files=4000 19 | opcache.fast_shutdown=1 20 | opcache.validate_timestamps=1 21 | opcache.revalidate_freq=0 22 | 23 | [memcache] 24 | #extension=memcache.so 25 | -------------------------------------------------------------------------------- /resources/conf/solr/4.x/README.md: -------------------------------------------------------------------------------- 1 | # Solr config 2 | 3 | These are the configuration files provides by the search_api_solr module: http://cgit.drupalcode.org/search_api_solr/tree/solr-conf/4.x 4 | -------------------------------------------------------------------------------- /resources/conf/solr/4.x/elevate.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 22 | 23 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /resources/conf/solr/4.x/mapping-ISOLatin1Accent.txt: -------------------------------------------------------------------------------- 1 | # This file contains character mappings for the default fulltext field type. 2 | # The source characters (on the left) will be replaced by the respective target 3 | # characters before any other processing takes place. 4 | # Lines starting with a pound character # are ignored. 5 | # 6 | # For sensible defaults, use the mapping-ISOLatin1Accent.txt file distributed 7 | # with the example application of your Solr version. 8 | # 9 | # Examples: 10 | # "À" => "A" 11 | # "\u00c4" => "A" 12 | # "\u00c4" => "\u0041" 13 | # "æ" => "ae" 14 | # "\n" => " " 15 | -------------------------------------------------------------------------------- /resources/conf/solr/4.x/protwords.txt: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # This file blocks words from being operated on by the stemmer and word delimiter. 3 | & 4 | < 5 | > 6 | ' 7 | " 8 | -------------------------------------------------------------------------------- /resources/conf/solr/4.x/schema_extra_fields.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 23 | 24 | -------------------------------------------------------------------------------- /resources/conf/solr/4.x/schema_extra_types.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 30 | 31 | -------------------------------------------------------------------------------- /resources/conf/solr/4.x/solrconfig_extra.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | textSpell 11 | 12 | 15 | 16 | 19 | 20 | default 21 | spell 22 | spellchecker 23 | true 24 | 27 | 28 | 29 | 33 | 41 | 42 | 43 | 53 | 54 | 61 | 69 | 70 | 71 | 80 | 81 | -------------------------------------------------------------------------------- /resources/conf/solr/4.x/solrcore.properties: -------------------------------------------------------------------------------- 1 | # Defines Solr properties for this specific core. 2 | solr.replication.master=false 3 | solr.replication.slave=false 4 | solr.replication.pollInterval=00:00:60 5 | solr.replication.masterUrl=http://localhost:8983/solr 6 | solr.replication.confFiles=schema.xml,mapping-ISOLatin1Accent.txt,protwords.txt,stopwords.txt,synonyms.txt,elevate.xml 7 | solr.mlt.timeAllowed=2000 8 | # You should not set your luceneMatchVersion to anything lower than your Solr 9 | # Version. 10 | solr.luceneMatchVersion=LUCENE_40 11 | solr.pinkPony.timeAllowed=-1 12 | # autoCommit after 10000 docs 13 | solr.autoCommit.MaxDocs=10000 14 | # autoCommit after 2 minutes 15 | solr.autoCommit.MaxTime=120000 16 | # autoSoftCommit after 2000 docs 17 | solr.autoSoftCommit.MaxDocs=2000 18 | # autoSoftCommit after 10 seconds 19 | solr.autoSoftCommit.MaxTime=10000 20 | solr.contrib.dir=../../../contrib 21 | -------------------------------------------------------------------------------- /resources/conf/solr/4.x/stopwords.txt: -------------------------------------------------------------------------------- 1 | # Contains words which shouldn't be indexed for fulltext fields, e.g., because 2 | # they're too common. For documentation of the format, see 3 | # http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters#solr.StopFilterFactory 4 | # (Lines starting with a pound character # are ignored.) 5 | -------------------------------------------------------------------------------- /resources/conf/solr/4.x/synonyms.txt: -------------------------------------------------------------------------------- 1 | # Contains synonyms to use for your index. For the format used, see 2 | # http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters#solr.SynonymFilterFactory 3 | # (Lines starting with a pound character # are ignored.) 4 | -------------------------------------------------------------------------------- /resources/conf/solr/6.x/elevate.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 | 22 | 23 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /resources/conf/solr/6.x/mapping-ISOLatin1Accent.txt: -------------------------------------------------------------------------------- 1 | # This file contains character mappings for the default fulltext field type. 2 | # The source characters (on the left) will be replaced by the respective target 3 | # characters before any other processing takes place. 4 | # Lines starting with a pound character # are ignored. 5 | # 6 | # For sensible defaults, use the mapping-ISOLatin1Accent.txt file distributed 7 | # with the example application of your Solr version. 8 | # 9 | # Examples: 10 | # "À" => "A" 11 | # "\u00c4" => "A" 12 | # "\u00c4" => "\u0041" 13 | # "æ" => "ae" 14 | # "\n" => " " 15 | -------------------------------------------------------------------------------- /resources/conf/solr/6.x/protwords.txt: -------------------------------------------------------------------------------- 1 | #----------------------------------------------------------------------- 2 | # This file blocks words from being operated on by the stemmer and word delimiter. 3 | & 4 | < 5 | > 6 | ' 7 | " 8 | -------------------------------------------------------------------------------- /resources/conf/solr/6.x/schema_extra_fields.xml: -------------------------------------------------------------------------------- 1 | 7 | 22 | -------------------------------------------------------------------------------- /resources/conf/solr/6.x/schema_extra_types.xml: -------------------------------------------------------------------------------- 1 | 10 | 33 | -------------------------------------------------------------------------------- /resources/conf/solr/6.x/schema_legacy_fields.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 61 | 62 | -------------------------------------------------------------------------------- /resources/conf/solr/6.x/schema_legacy_types.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /resources/conf/solr/6.x/solrconfig_spellcheck.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | textSpell 12 | 13 | 14 | 17 | 18 | 19 | 20 | default 21 | spell 22 | solr.DirectSolrSpellChecker 23 | 24 | internal 25 | 26 | 0.5 27 | 28 | 2 29 | 30 | 1 31 | 32 | 5 33 | 34 | 4 35 | 36 | 0.01 37 | 40 | 41 | 42 | 43 | 44 | wordbreak 45 | solr.WordBreakSolrSpellChecker 46 | name 47 | true 48 | true 49 | 10 50 | 51 | 52 | 53 | 63 | 64 | 71 | 78 | 79 | 80 | 89 | 90 | -------------------------------------------------------------------------------- /resources/conf/solr/6.x/solrcore.properties: -------------------------------------------------------------------------------- 1 | # Defines Solr properties for this specific core. 2 | solr.replication.master=false 3 | solr.replication.slave=false 4 | solr.replication.pollInterval=00:00:60 5 | solr.replication.masterUrl=http://localhost:8983/solr 6 | solr.replication.confFiles=schema.xml,mapping-ISOLatin1Accent.txt,protwords.txt,stopwords.txt,synonyms.txt,elevate.xml 7 | solr.mlt.timeAllowed=2000 8 | # You should not set your luceneMatchVersion to anything lower than your Solr 9 | # Version. 10 | solr.luceneMatchVersion=6.0 11 | solr.selectSearchHandler.timeAllowed=-1 12 | # autoCommit after 10000 docs 13 | solr.autoCommit.MaxDocs=10000 14 | # autoCommit after 2 minutes 15 | solr.autoCommit.MaxTime=120000 16 | # autoSoftCommit after 2000 docs 17 | solr.autoSoftCommit.MaxDocs=2000 18 | # autoSoftCommit after 10 seconds 19 | solr.autoSoftCommit.MaxTime=10000 20 | solr.install.dir=../../.. 21 | -------------------------------------------------------------------------------- /resources/conf/solr/6.x/stopwords.txt: -------------------------------------------------------------------------------- 1 | # Contains words which shouldn't be indexed for fulltext fields, e.g., because 2 | # they're too common. For documentation of the format, see 3 | # http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters#solr.StopFilterFactory 4 | # (Lines starting with a pound character # are ignored.) 5 | -------------------------------------------------------------------------------- /resources/conf/solr/6.x/synonyms.txt: -------------------------------------------------------------------------------- 1 | # Contains synonyms to use for your index. For the format used, see 2 | # http://wiki.apache.org/solr/AnalyzersTokenizersTokenFilters#solr.SynonymFilterFactory 3 | # (Lines starting with a pound character # are ignored.) 4 | -------------------------------------------------------------------------------- /resources/drupal-enable-profiling.patch: -------------------------------------------------------------------------------- 1 | diff --git a/index.php b/index.php 2 | index 8b83199..021ba09 100644 3 | --- a/index.php 4 | +++ b/index.php 5 | @@ -11,6 +11,15 @@ 6 | * See COPYRIGHT.txt and LICENSE.txt. 7 | */ 8 | 9 | +// Enable XHProf profile collection: 10 | +xhprof_sample_enable(); 11 | +register_shutdown_function(function () { 12 | + $url_parts = implode("_", arg()); 13 | + $filename = '/var/platform/xhprof/' . $url_parts . '.' . uniqid() . '.sample_xhprof'; 14 | + file_put_contents($filename, serialize(xhprof_sample_disable())); 15 | + chmod($filename, 0777); 16 | +}); 17 | + 18 | /** 19 | * Root directory of Drupal installation. 20 | */ 21 | -------------------------------------------------------------------------------- /resources/images/php/5.6/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:5.6.8-fpm 2 | MAINTAINER Matt Glaman 3 | # Install modules 4 | RUN apt-get update && apt-get install -y \ 5 | libfreetype6-dev \ 6 | libjpeg62-turbo-dev \ 7 | libmcrypt-dev \ 8 | libpng12-dev \ 9 | libxml2-dev \ 10 | && docker-php-ext-install mcrypt pdo_mysql mysql mysqli mbstring opcache soap bcmath \ 11 | && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \ 12 | && docker-php-ext-install gd 13 | 14 | # Make memcache available 15 | RUN curl -L -o /root/memcache.tgz https://pecl.php.net/get/memcache-3.0.6.tgz && \ 16 | cd /root && \ 17 | tar -zxvf memcache.tgz && \ 18 | cd /root/memcache-3.0.6 && \ 19 | /usr/local/bin/phpize && \ 20 | ./configure --with-php-config=/usr/local/bin/php-config && \ 21 | make && \ 22 | make install && \ 23 | cd /root && \ 24 | rm -fr /root/memcache-3.0.6 && \ 25 | rm -fr /root/memcache.tgz 26 | 27 | # Setup xdebug 28 | RUN curl -L -o /root/xdebug.tgz https://pecl.php.net/get/xdebug-2.3.2.tgz && \ 29 | cd /root && \ 30 | tar -zxvf xdebug.tgz && \ 31 | cd /root/xdebug-2.3.2 && \ 32 | /usr/local/bin/phpize && \ 33 | ./configure --enable-xdebug --with-php-config=/usr/local/bin/php-config && \ 34 | make && \ 35 | make install && \ 36 | cd /root && \ 37 | rm -fr /root/xdebug-2.3.2 && \ 38 | rm -fr /root/xdebug.tgz 39 | 40 | # Setup xhprof 41 | RUN curl -L -o /root/xhprof.tgz https://pecl.php.net/get/xhprof-0.9.4.tgz && \ 42 | cd /root && \ 43 | tar -zxvf xhprof.tgz && \ 44 | cd /root/xhprof-0.9.4/extension && \ 45 | /usr/local/bin/phpize && \ 46 | ./configure --with-php-config=/usr/local/bin/php-config && \ 47 | make && \ 48 | make install && \ 49 | cd /root && \ 50 | rm -fr /root/xhprof-0.9.4.tgz && \ 51 | rm -fr /root/xhprof.tgz 52 | 53 | # Setup redis 54 | RUN pecl install redis 55 | 56 | RUN export VERSION=`php -r "echo PHP_MAJOR_VERSION.PHP_MINOR_VERSION;"` \ 57 | && curl -A "Docker" -o /tmp/blackfire-probe.tar.gz -D - -L -s https://blackfire.io/api/v1/releases/probe/php/linux/amd64/${VERSION} \ 58 | && tar zxpf /tmp/blackfire-probe.tar.gz -C /tmp \ 59 | && mv /tmp/blackfire-*.so `php -r "echo ini_get('extension_dir');"`/blackfire.so \ 60 | && echo "extension=blackfire.so\nblackfire.agent_socket=\${BLACKFIRE_PORT}" > $PHP_INI_DIR/conf.d/blackfire.ini 61 | 62 | CMD ["php-fpm"] 63 | -------------------------------------------------------------------------------- /resources/images/php/7.0/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.0-fpm 2 | MAINTAINER Matt Glaman 3 | # Install modules 4 | RUN apt-get update && apt-get install -y \ 5 | libfreetype6-dev \ 6 | libjpeg62-turbo-dev \ 7 | libmcrypt-dev \ 8 | libpng12-dev \ 9 | libxml2-dev 10 | RUN docker-php-ext-install mcrypt pdo_mysql mysqli mbstring opcache soap bcmath 11 | RUN docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \ 12 | && docker-php-ext-install gd 13 | 14 | # Make memcache available 15 | #RUN curl -L -o /root/memcache.tgz https://pecl.php.net/get/memcache-3.0.6.tgz && \ 16 | # cd /root && \ 17 | # tar -zxvf memcache.tgz && \ 18 | # cd /root/memcache-3.0.6 && \ 19 | # /usr/local/bin/phpize && \ 20 | # ./configure --with-php-config=/usr/local/bin/php-config && \ 21 | # make && \ 22 | # make install && \ 23 | # cd /root && \ 24 | # rm -fr /root/memcache-3.0.6 && \ 25 | # rm -fr /root/memcache.tgz 26 | 27 | # Setup xdebug 28 | RUN pecl install xdebug && docker-php-ext-enable xdebug 29 | 30 | # Setup redis 31 | RUN pecl install redis && docker-php-ext-enable redis 32 | 33 | # Install APCu and APC backward compatibility 34 | RUN pecl install apcu \ 35 | && pecl install apcu_bc-1.0.3 \ 36 | && docker-php-ext-enable apcu --ini-name 10-docker-php-ext-apcu.ini \ 37 | && docker-php-ext-enable apc --ini-name 20-docker-php-ext-apc.ini 38 | 39 | # XHPROF is not available in PHP 7. 40 | 41 | RUN export VERSION=`php -r "echo PHP_MAJOR_VERSION.PHP_MINOR_VERSION;"` \ 42 | && curl -A "Docker" -o /tmp/blackfire-probe.tar.gz -D - -L -s https://blackfire.io/api/v1/releases/probe/php/linux/amd64/${VERSION} \ 43 | && tar zxpf /tmp/blackfire-probe.tar.gz -C /tmp \ 44 | && mv /tmp/blackfire-*.so `php -r "echo ini_get('extension_dir');"`/blackfire.so \ 45 | && echo "extension=blackfire.so\nblackfire.agent_socket=\${BLACKFIRE_PORT}" > $PHP_INI_DIR/conf.d/blackfire.ini 46 | 47 | CMD ["php-fpm"] 48 | -------------------------------------------------------------------------------- /resources/images/php/7.1/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:7.1-fpm 2 | MAINTAINER Matt Glaman 3 | # Install modules 4 | RUN apt-get update && apt-get install -y \ 5 | libfreetype6-dev \ 6 | libjpeg62-turbo-dev \ 7 | libmcrypt-dev \ 8 | libpng12-dev \ 9 | libxml2-dev 10 | RUN docker-php-ext-install mcrypt pdo_mysql mysqli mbstring opcache soap bcmath 11 | RUN docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \ 12 | && docker-php-ext-install gd 13 | 14 | # Make memcache available 15 | #RUN curl -L -o /root/memcache.tgz https://pecl.php.net/get/memcache-3.0.6.tgz && \ 16 | # cd /root && \ 17 | # tar -zxvf memcache.tgz && \ 18 | # cd /root/memcache-3.0.6 && \ 19 | # /usr/local/bin/phpize && \ 20 | # ./configure --with-php-config=/usr/local/bin/php-config && \ 21 | # make && \ 22 | # make install && \ 23 | # cd /root && \ 24 | # rm -fr /root/memcache-3.0.6 && \ 25 | # rm -fr /root/memcache.tgz 26 | 27 | # Setup xdebug 28 | RUN pecl install xdebug && docker-php-ext-enable xdebug 29 | 30 | # Setup redis 31 | RUN pecl install redis && docker-php-ext-enable redis 32 | 33 | # Install APCu and APC backward compatibility 34 | RUN pecl install apcu \ 35 | && pecl install apcu_bc-1.0.3 \ 36 | && docker-php-ext-enable apcu --ini-name 10-docker-php-ext-apcu.ini \ 37 | && docker-php-ext-enable apc --ini-name 20-docker-php-ext-apc.ini 38 | 39 | # XHPROF is not available in PHP 7. 40 | 41 | RUN export VERSION=`php -r "echo PHP_MAJOR_VERSION.PHP_MINOR_VERSION;"` \ 42 | && curl -A "Docker" -o /tmp/blackfire-probe.tar.gz -D - -L -s https://blackfire.io/api/v1/releases/probe/php/linux/amd64/${VERSION} \ 43 | && tar zxpf /tmp/blackfire-probe.tar.gz -C /tmp \ 44 | && mv /tmp/blackfire-*.so `php -r "echo ini_get('extension_dir');"`/blackfire.so \ 45 | && echo "extension=blackfire.so\nblackfire.agent_socket=\${BLACKFIRE_PORT}" > $PHP_INI_DIR/conf.d/blackfire.ini 46 | 47 | CMD ["php-fpm"] 48 | -------------------------------------------------------------------------------- /resources/ssl/nginx.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDtTCCAp2gAwIBAgIJANDoDPvRQJWWMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV 3 | BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX 4 | aWRnaXRzIFB0eSBMdGQwHhcNMTUwOTI0MjAxMzAzWhcNMTYwOTIzMjAxMzAzWjBF 5 | MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 6 | ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 7 | CgKCAQEA1VoDoRRwbuT7IcOj1dE++67Uo6jVe+N+/Tzl7L8Q1/Gih45Yo0tKb5UH 8 | 2ZTbwUnxhqNthEdqeNM8bIjX6MzqjYTlNy7DlTCxUmPXJZMQZPi8t7Osn3VtWBYX 9 | 8QnLIibgZ1GIGkWzcsG3oaIpaDV++1At6Y7bJbJ9Z+ciixN6H2Y0WuLwrHcgVw0L 10 | s79xC6+AG37h2aRvDdQvzbsKyhF9Q1TVx5EXlXV9rGKRXyGdpJ4P+jJ16zyhwrsh 11 | 3nwTrFXgnxxAokmxIrvUCwDW9tnWbDN9njbiHMQqXxHhSEnvwVoZOySltdtO+Sax 12 | 2dG2OGe5cyNyrolLteH043ayRnRdtQIDAQABo4GnMIGkMB0GA1UdDgQWBBTJTUVx 13 | ihsMMtJX9A8jlq/VI25okTB1BgNVHSMEbjBsgBTJTUVxihsMMtJX9A8jlq/VI25o 14 | kaFJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV 15 | BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJANDoDPvRQJWWMAwGA1UdEwQF 16 | MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAIiLv4RUKnWW5Znvr0Eml7jFBRxZh6Pp 17 | 7lrPxxrGmGuwiV81KTxNLjXdkzMHD0xlWwTEajsMOQpZTs+NRR7I8qIhLFb+Lif6 18 | fdPXNZ2kaCnGk9q8tCc0fBXR6vVdXqMFT9Ln/IZDLYiYOx1QTkVpEhrGPqD0eQ1Z 19 | +qDTZOzcrJW2uLAYWfgen2TK3WngLW+WrZ9U9YUdIMmW4aUxco52eBZ4fcXWBt6R 20 | U7OQfbBXxwtN4JgAJIKfES/gAMf8YeEUSCOkqq2TAHrTrHdvtXxC5hPC0DfhcN1Z 21 | mKpYwlEimRrPhkqzb/eLGcOagjLH+WWhXf5i81r8qxRE032d90oWzaY= 22 | -----END CERTIFICATE----- 23 | -------------------------------------------------------------------------------- /resources/ssl/nginx.key: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEpAIBAAKCAQEA1VoDoRRwbuT7IcOj1dE++67Uo6jVe+N+/Tzl7L8Q1/Gih45Y 3 | o0tKb5UH2ZTbwUnxhqNthEdqeNM8bIjX6MzqjYTlNy7DlTCxUmPXJZMQZPi8t7Os 4 | n3VtWBYX8QnLIibgZ1GIGkWzcsG3oaIpaDV++1At6Y7bJbJ9Z+ciixN6H2Y0WuLw 5 | rHcgVw0Ls79xC6+AG37h2aRvDdQvzbsKyhF9Q1TVx5EXlXV9rGKRXyGdpJ4P+jJ1 6 | 6zyhwrsh3nwTrFXgnxxAokmxIrvUCwDW9tnWbDN9njbiHMQqXxHhSEnvwVoZOySl 7 | tdtO+Sax2dG2OGe5cyNyrolLteH043ayRnRdtQIDAQABAoIBAQDBwqkvHj/qBp0o 8 | keP2KL25l5LYO1n+1KIc3LS36OIkK/f+gkFvs84bPk7NxQo7JatrKnsoui8xIi5r 9 | J3BmpcuS0RHWpurLUFiKSdA3+LLX8Kp7b3sjqmKusAn4R3urPYFpc7tWmNybKOFb 10 | sslGMedr1+qKhcnzF4Ia3egaRIsRfXf7AAZXRrpopsOCABFvFy79lLVxnGRaCPVy 11 | wCbVfOKGVmcLJzf2DCvKkx059V5bTj7htutwbnkzc6BFrG52Y5tLJsl7OP6yIrdY 12 | 85ijsayP3pw5/yBwTfQaM/B0NanQoPtGvRPNIal56Es8d3ZlhJ7BPIHzTys28lEY 13 | mQsPt7bhAoGBAPvRNrypAiEh354lLI570uDVTsXQ5weFoSQ7K7OauV11O8uxokO5 14 | YrvvyZrImwGcWXj953o0ITlzbWsJHg1DIhPWVd/Mw2RqSrCJQ/P6qHd799hGy4Sr 15 | DewI3+L8zAEDKGDXM1imgGM5u6ZR1M+PyNwyQtQ9ya//P7a/IOSWokN5AoGBANjl 16 | PEgTulvK52nG+OS5iYp1J0iQAvv3k4bGnB3kEjEbjZ22KZXGl7+7xI5kcwVN5Yib 17 | YVtvj+jwsj8AkKLU4IFaDwjqDmWeh7I2MVZyvEVg72qNAGb3DwWQr7i+pEkrRJTK 18 | f2QzAnpjdwO3kG5TV3WyADJ3F5ccVh7v/nClOUEdAoGBAOVPA9hS0xSWwwpA8c0i 19 | JssEk4IBlJA9+JqEo2wfJlNbsULxFEE1rRpU5jJkIPsbhEJwe/zFQcgOO4JvwAT1 20 | sC9+S57L2NY1EKSnqtCe3hBIPE6fUS8TkU7yWeZaq+70EC33adN9c4ZoCWAQPolw 21 | dslFn6OkTGhPOJbSf0KJhWOJAoGACXmiM4fhGUd46o8NqnhMgObzS2E3MCi1oiql 22 | AU1IWirY+e3OTUO6t16NjKbou2+WumwHScZK5CM3tjbhGqvwDtaPV5VaAah2RxvE 23 | 9gDXJrTAYMg+H0Flv5l9g7WYWbXxLgrWm9r6nbwgo+HuBlPPu02GYq3/MtPDE2Dq 24 | naMqKCUCgYB70b45tPHY94GkhOLk21+FbONp7Ue+PRbkhn9SpNH4oPTO63eGrBTf 25 | fYawMew27s5aBRDgGt7fSxyxd1w/cVV/KvsYBe0glXGXzFGrLoVYM9ZTssd379Ef 26 | o+IOo3gkMrclA2UOwme3gA6LryU9Ri3pFlX1WTPF3ZLCBVEzxaBHKQ== 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /resources/stacks/drupal7/drushrc.php: -------------------------------------------------------------------------------- 1 | 'mysql', 14 | 'host' => '{{ container_name }}', 15 | 'username' => 'mysql', 16 | 'password' => 'mysql', 17 | 'database' => 'data', 18 | 'prefix' => '', 19 | ); 20 | 21 | // Redis configuration. 22 | $conf['redis_client_host'] = '{{ redis_container_name }}'; // Your Redis instance hostname. 23 | 24 | // Configuration when running drush commands locally. 25 | if (empty($_SERVER['PLATFORM_DOCKER'])) { 26 | 27 | $conf['redis_client_host'] = '127.0.0.1'; 28 | $conf['redis_client_port'] = trim(shell_exec("docker inspect --format='{{(index (index .NetworkSettings.Ports \"6379/tcp\") 0).HostPort}}' {{ redis_container_name }}")); 29 | 30 | $port_cmd = "docker inspect --format='{{(index (index .NetworkSettings.Ports \"3306/tcp\") 0).HostPort}}' {{ container_name }}"; 31 | $port = trim(shell_exec($port_cmd)); 32 | 33 | // Default config within Docker container. 34 | $databases['default']['default'] = array( 35 | 'driver' => 'mysql', 36 | 'host' => '127.0.0.1', 37 | 'port' => $port, 38 | 'username' => 'mysql', 39 | 'password' => 'mysql', 40 | 'database' => 'data', 41 | 'prefix' => '', 42 | ); 43 | } 44 | 45 | // Set the private file path to where a "platform build" command creates one. 46 | $conf['file_private_path'] = '../private'; 47 | -------------------------------------------------------------------------------- /resources/stacks/drupal7/settings.php: -------------------------------------------------------------------------------- 1 | 404 Not Found

Not Found

The requested URL "@path" was not found on this server.

'; 22 | 23 | $local_settings = dirname(__FILE__) . '/settings.local.php'; 24 | if (file_exists($local_settings)) { 25 | include $local_settings; 26 | } 27 | -------------------------------------------------------------------------------- /resources/stacks/drupal8/drushrc.php: -------------------------------------------------------------------------------- 1 | 'mysql', 22 | 'host' => '{{ container_name }}', 23 | 'username' => 'mysql', 24 | 'password' => 'mysql', 25 | 'database' => 'data', 26 | 'prefix' => '', 27 | 'port' => 3306, 28 | 'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', 29 | ); 30 | 31 | $redis_containter_name = "{{ redis_container_name }}"; 32 | if ($redis_containter_name) { 33 | // Set Redis as the default backend for any cache bin not otherwise specified. 34 | $settings['cache']['default'] = 'cache.backend.redis'; 35 | $settings['redis.connection']['host'] = $redis_containter_name; 36 | $settings['redis.connection']['port'] = '6379'; 37 | 38 | // Apply changes to the container configuration to better leverage Redis. 39 | // This includes using Redis for the lock and flood control systems, as well 40 | // as the cache tag checksum. Alternatively, copy the contents of that file 41 | // to your project-specific services.yml file, modify as appropriate, and 42 | // remove this line. 43 | $settings['container_yamls'][] = 'modules/contrib/redis/example.services.yml'; 44 | 45 | // Allow the services to work before the Redis module itself is enabled. 46 | $settings['container_yamls'][] = 'modules/contrib/redis/redis.services.yml'; 47 | 48 | // Manually add the classloader path, this is required for the container cache bin definition below 49 | // and allows to use it without the redis module being enabled. 50 | $class_loader->addPsr4('Drupal\\redis\\', 'modules/contrib/redis/src'); 51 | 52 | // Use redis for container cache. 53 | // The container cache is used to load the container definition itself, and 54 | // thus any configuration stored in the container itself is not available 55 | // yet. These lines force the container cache to use Redis rather than the 56 | // default SQL cache. 57 | $settings['bootstrap_container_definition'] = [ 58 | 'parameters' => [], 59 | 'services' => [ 60 | 'redis.factory' => [ 61 | 'class' => 'Drupal\redis\ClientFactory', 62 | ], 63 | 'cache.backend.redis' => [ 64 | 'class' => 'Drupal\redis\Cache\CacheBackendFactory', 65 | 'arguments' => ['@redis.factory', '@cache_tags_provider.container'], 66 | ], 67 | 'cache.container' => [ 68 | 'class' => '\Drupal\redis\Cache\PhpRedis', 69 | 'factory' => ['@cache.backend.redis', 'get'], 70 | 'arguments' => ['container'], 71 | ], 72 | 'cache_tags_provider.container' => [ 73 | 'class' => 'Drupal\redis\Cache\RedisCacheTagsChecksum', 74 | 'arguments' => ['@redis.factory'], 75 | ], 76 | ], 77 | ]; 78 | 79 | // Set a fixed prefix so that all requests share the same prefix, even if 80 | // on different domains. 81 | $settings['cache_prefix'] = 'prefix_'; 82 | } 83 | 84 | // Configuration when running drush commands locally. 85 | if (empty($_SERVER['PLATFORM_DOCKER'])) { 86 | $cmd = "docker inspect --format='{{(index (index .NetworkSettings.Ports \"3306/tcp\") 0).HostPort}}' {{ container_name }}"; 87 | $port = trim(shell_exec($cmd)); 88 | $host = '{{ project_domain }}'; 89 | 90 | // Default config within Docker container. 91 | $databases['default']['default'] = array( 92 | 'driver' => 'mysql', 93 | 'host' => '127.0.0.1', 94 | 'port' => $port, 95 | 'username' => 'mysql', 96 | 'password' => 'mysql', 97 | 'database' => 'data', 98 | 'prefix' => '', 99 | 'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', 100 | 101 | ); 102 | 103 | if ($redis_containter_name) { 104 | $settings['redis.connection']['host'] = '127.0.0.1'; 105 | $settings['redis.connection']['port'] = trim(shell_exec("docker inspect --format='{{(index (index .NetworkSettings.Ports \"6379/tcp\") 0).HostPort}}' {{ redis_container_name }}")); 106 | } 107 | } 108 | 109 | // Set the private file path to where a "platform build" command creates one. 110 | $settings['file_private_path'] = '../private'; 111 | 112 | // Configuration directories. 113 | $config_directories = array( 114 | CONFIG_ACTIVE_DIRECTORY => '../shared/config/active', 115 | CONFIG_STAGING_DIRECTORY => '../shared/config/staging', 116 | CONFIG_SYNC_DIRECTORY => '../shared/config/staging', 117 | ); 118 | -------------------------------------------------------------------------------- /resources/stacks/drupal8/settings.php: -------------------------------------------------------------------------------- 1 | setDefaultTimezone(); 22 | $this->addCommands($this->getCommands()); 23 | } 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | protected function getDefaultCommands() 29 | { 30 | return array(new HelpCommand(), new Command\ListCommand()); 31 | } 32 | 33 | /** 34 | * @return \Symfony\Component\Console\Command\Command[] 35 | */ 36 | public function getCommands() 37 | { 38 | static $commands = array(); 39 | if (count($commands)) { 40 | return $commands; 41 | } 42 | 43 | $commands[] = new Command\InitCommand(); 44 | $commands[] = new Command\LinkCommand(); 45 | $commands[] = new Command\DrushCommand(); 46 | $commands[] = new Command\Docker\UpCommand(); 47 | $commands[] = new Command\Docker\StopCommand(); 48 | $commands[] = new Command\Docker\SshCommand(); 49 | $commands[] = new Command\Docker\LogsCommand(); 50 | $commands[] = new Command\Docker\ProxyCommand(); 51 | $commands[] = new Command\Docker\RebuildCommand(); 52 | $commands[] = new Command\Docker\RestartCommand(); 53 | $commands[] = new Command\Flamegraph\SetupCommand(); 54 | $commands[] = new Command\Flamegraph\CreateCommand(); 55 | $commands[] = new Command\Flamegraph\UnpatchCommand(); 56 | $commands[] = new Command\Project\DbSyncCommand(); 57 | $commands[] = new Command\Project\BehatCommand(); 58 | $commands[] = new Command\Providers\PlatformshProviderCommand(); 59 | $commands[] = new Command\Providers\Commerce2xProviderCommand(); 60 | return $commands; 61 | } 62 | 63 | /** 64 | * {@inheritdoc} 65 | */ 66 | protected function getDefaultHelperSet() 67 | { 68 | return new HelperSet(array( 69 | new FormatterHelper(), 70 | new DebugFormatterHelper(), 71 | new ProcessHelper(), 72 | new QuestionHelper(), 73 | )); 74 | } 75 | 76 | 77 | /** 78 | * Set the default timezone. 79 | * 80 | * PHP 5.4 has removed the autodetection of the system timezone, 81 | * so it needs to be done manually. 82 | * UTC is the fallback in case autodetection fails. 83 | */ 84 | protected function setDefaultTimezone() 85 | { 86 | $timezone = 'UTC'; 87 | if (is_link('/etc/localtime')) { 88 | // Mac OS X (and older Linuxes) 89 | // /etc/localtime is a symlink to the timezone in /usr/share/zoneinfo. 90 | $filename = readlink('/etc/localtime'); 91 | if (strpos($filename, '/usr/share/zoneinfo/') === 0) { 92 | $timezone = substr($filename, 20); 93 | } 94 | } elseif (file_exists('/etc/timezone')) { 95 | // Ubuntu / Debian. 96 | $data = file_get_contents('/etc/timezone'); 97 | if ($data) { 98 | $timezone = trim($data); 99 | } 100 | } elseif (file_exists('/etc/sysconfig/clock')) { 101 | // RHEL/CentOS 102 | $data = parse_ini_file('/etc/sysconfig/clock'); 103 | if (!empty($data['ZONE'])) { 104 | $timezone = trim($data['ZONE']); 105 | } 106 | } 107 | date_default_timezone_set($timezone); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/BrowserTrait.php: -------------------------------------------------------------------------------- 1 | getDefaultBrowser(); 20 | if ($browser) { 21 | $opened = shell_exec("$browser $url 2>&1; echo $?"); 22 | if ($opened == 0) { 23 | $stdErr->writeln("Opened: $url"); 24 | return; 25 | } 26 | } else { 27 | $stdErr->writeln("Browser not found: $browser"); 28 | } 29 | $stdOut->writeln($url); 30 | } 31 | 32 | /** 33 | * Find a default browser to use. 34 | * 35 | * @return string|false 36 | */ 37 | protected function getDefaultBrowser() 38 | { 39 | $potential = array('xdg-open', 'open', 'start'); 40 | foreach ($potential as $browser) { 41 | // Check if command exists by executing help flag. 42 | if (shell_exec("command -v $browser; echo $?") == 0) { 43 | return $browser; 44 | } 45 | } 46 | return false; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Command/Command.php: -------------------------------------------------------------------------------- 1 | stdOut = $output; 30 | $this->stdErr = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output; 31 | $this->stdIn = $input; 32 | self::$interactive = $input->isInteractive(); 33 | 34 | // Check if this command requires a project to be defined in order to run. 35 | $this->checkProjectRequired(); 36 | } 37 | 38 | protected function checkProjectRequired() { 39 | if ($this->projectRequired && empty(Config::get())) { 40 | $this->getApplication()->find('init')->run($this->stdIn, $this->stdOut); 41 | exit(1); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Command/Docker/DockerCommand.php: -------------------------------------------------------------------------------- 1 | stdOut->writeln("Cannot find docker command"); 32 | exit(1); 33 | } 34 | if (!Compose::exists()) { 35 | $this->stdOut->writeln("Cannot find docker-compose command"); 36 | exit(1); 37 | } 38 | 39 | // For Mac OS X we need to ensure a Docker VM is running. 40 | if (!Docker::native()) { 41 | $this->validateNonNative(); 42 | } else { 43 | $this->validateNative(); 44 | } 45 | } 46 | 47 | protected function validateNative() 48 | { 49 | // Give a Docker command a try. 50 | if (!Docker::available()) { 51 | $this->stdOut->writeln("Unable to reach Docker service - try running `service docker start`"); 52 | exit(1); 53 | } 54 | } 55 | 56 | protected function validateNonNative() 57 | { 58 | if (!Docker::available()) { 59 | // Check to see if Docker has been exported. 60 | if (!$this->envExported()) { 61 | $this->stdOut->writeln("Docker environment information not exported. Attempting from PLATFORM_DOCKER_MACHINE_NAME"); 62 | if (getenv('PLATFORM_DOCKER_MACHINE_NAME')) { 63 | // Attempt to boot the Docker VM 64 | if (!Machine::start(getenv('PLATFORM_DOCKER_MACHINE_NAME'))) { 65 | $this->stdOut->writeln("Failed to start Docker machine"); 66 | exit(1); 67 | } 68 | } else { 69 | $this->stdOut->writeln("You need to start your Docker machine and export the environment information"); 70 | exit(1); 71 | } 72 | 73 | // Export the Docker VM info on behalf of the user 74 | $this->dockerParams = Machine::getEnv(getenv('PLATFORM_DOCKER_MACHINE_NAME')); 75 | foreach ($this->dockerParams as $key => $value) { 76 | putenv("$key=$value"); 77 | } 78 | } 79 | // Give a Docker command a try. 80 | if (!Docker::available()) { 81 | $this->stdOut->writeln("Unable to reach Docker service - try manually exporting environment variables."); 82 | exit(1); 83 | } 84 | } 85 | } 86 | 87 | /** 88 | * @return bool 89 | */ 90 | protected function envExported() { 91 | return (bool) !empty($this->dockerParams) || Machine::isExported(); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/Command/Docker/LogsCommand.php: -------------------------------------------------------------------------------- 1 | setName('docker:logs') 25 | ->setDescription('Tails the logs of a specific service container') 26 | ->addArgument( 27 | 'service', 28 | InputArgument::REQUIRED, 29 | 'Service to SSH into the container of: http, php, db, redis, solr'); 30 | } 31 | 32 | /** 33 | * {@inheritdoc} 34 | */ 35 | protected function execute(InputInterface $input, OutputInterface $output) 36 | { 37 | $containerName = null; 38 | $type = $input->getArgument('service'); 39 | switch ($type) { 40 | case 'http': 41 | Compose::logs(['nginx']); 42 | break; 43 | case 'php': 44 | Compose::logs(['phpfpm']); 45 | break; 46 | case 'db': 47 | Compose::logs(['mariadb']); 48 | break; 49 | case 'redis': 50 | Compose::logs(['redis']); 51 | break; 52 | case 'solr': 53 | Compose::logs(['solr']); 54 | break; 55 | default: 56 | $this->stdOut->writeln("Invalid service type"); 57 | break; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Command/Docker/ProxyCommand.php: -------------------------------------------------------------------------------- 1 | setName('docker:proxy') 28 | ->setAliases(['proxy']) 29 | ->addArgument( 30 | 'operation', 31 | InputArgument::OPTIONAL, 32 | 'Allows you to start, stop, create container, or update image for the nginx container proxy', 33 | 'start' 34 | ) 35 | ->setDescription('Starts the nginx proxy container'); 36 | } 37 | 38 | /** 39 | * {@inheritdoc} 40 | */ 41 | protected function execute(InputInterface $input, OutputInterface $output) 42 | { 43 | switch($input->getArgument('operation')) { 44 | case 'stop': 45 | return $this->stopProxy(); 46 | case 'start': 47 | return $this->startProxy(); 48 | case 'create': 49 | return $this->createProxy(); 50 | case 'update': 51 | return $this->updateImage(); 52 | default: 53 | throw new \InvalidArgumentException('You must specify start or stop.'); 54 | } 55 | } 56 | 57 | protected function stopProxy() 58 | { 59 | $this->stdOut->writeln("Stopping the nginx proxy container"); 60 | return Docker::stop([$this->containerName]); 61 | } 62 | 63 | protected function startProxy() 64 | { 65 | $this->stdOut->writeln("Starting the nginx proxy container"); 66 | try { 67 | // Throws an exception if not successful. 68 | Docker::start([$this->containerName]); 69 | } catch (\Exception $e) { 70 | return $this->createProxy(); 71 | } 72 | 73 | return 1; 74 | } 75 | 76 | protected function createProxy() 77 | { 78 | $this->stdOut->writeln("Creating the nginx proxy container"); 79 | return Docker::run([ 80 | '-d', 81 | '-p', 82 | '80:80', 83 | '-v', 84 | '/var/run/docker.sock:/tmp/docker.sock:ro', 85 | '--name', 86 | $this->containerName, 87 | 'jwilder/nginx-proxy', 88 | ]); 89 | } 90 | 91 | protected function updateImage() 92 | { 93 | try { 94 | $this->stopProxy(); 95 | $this->stdOut->writeln("Removing nginx proxy container"); 96 | Docker::rm(['nginx-proxy']); 97 | } catch (\Exception $e) { } 98 | 99 | Docker::pull(['jwilder/nginx-proxy']); 100 | $this->createProxy(); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/Command/Docker/RebuildCommand.php: -------------------------------------------------------------------------------- 1 | setName('docker:rebuild') 34 | ->setAliases(['rebuild']) 35 | ->setDescription('Rebuild configurations and containers'); 36 | } 37 | 38 | /** 39 | * {@inheritdoc} 40 | */ 41 | protected function execute(InputInterface $input, OutputInterface $output) 42 | { 43 | $platform_config = new PlatformAppConfig(); 44 | $composeConfig = new ComposeConfig($platform_config->getPhpVersion()); 45 | 46 | // Create docker folder in project. 47 | try { 48 | $composeConfig->ensureDirectories(); 49 | } catch (IOException $e) { 50 | $this->stdOut->writeln("Error while trying to create docker-compose directories."); 51 | exit(1); 52 | } 53 | 54 | $composeConfig->copyImages(); 55 | $composeConfig->copyConfigs(); 56 | 57 | $composeContainers = new ComposeContainers(Platform::rootDir(), Config::get('name')); 58 | 59 | // @todo: With #20 and making tool provider aware, read those configs. Or push those configs to main. 60 | if (isset(Config::get()['services'])) { 61 | foreach(Config::get('services') as $service) { 62 | switch($service) { 63 | case 'redis': 64 | $composeContainers->addRedis(); 65 | break; 66 | case 'solr': 67 | $composeContainers->addSolr(); 68 | break; 69 | case 'memcached': 70 | $composeContainers->addMemcached(); 71 | break; 72 | case 'blackfire': 73 | $composeContainers->addBlackfire(); 74 | break; 75 | } 76 | } 77 | } 78 | // Support services defined in .platform/services.yaml 79 | else { 80 | if (PlatformAppConfig::hasRedis()) { 81 | $composeContainers->addRedis(); 82 | } 83 | if (PlatformAppConfig::hasSolr()) { 84 | $composeContainers->addSolr(); 85 | } 86 | } 87 | 88 | $composeConfig->writeDockerCompose($composeContainers); 89 | 90 | $stack = Toolstack::inspect(Platform::webDir()); 91 | if ($stack) { 92 | $this->stdOut->writeln("Configuring stack: " . $stack->type()); 93 | StacksFactory::configure($stack->type()); 94 | } 95 | 96 | // Stop and remove any existing containers. 97 | Compose::stop(); 98 | Compose::rm(TRUE); 99 | 100 | $this->stdOut->writeln('Building the containers'); 101 | Compose::build(); 102 | 103 | $this->stdOut->writeln('Bringing up the containers'); 104 | Compose::up(['-d']); 105 | $uri = Platform::getUri(); 106 | $name = Platform::projectName(); 107 | $this->stdOut->writeln("$name available at $uri"); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/Command/Docker/RestartCommand.php: -------------------------------------------------------------------------------- 1 | setName('docker:restart') 24 | ->setAliases(['reboot']) 25 | ->setDescription('Restarts the docker containers'); 26 | } 27 | 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | protected function execute(InputInterface $input, OutputInterface $output) 32 | { 33 | $this->stdOut->writeln("Restarting containers"); 34 | Compose::restart(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Command/Docker/SshCommand.php: -------------------------------------------------------------------------------- 1 | setName('docker:ssh') 29 | ->setAliases(['ssh']) 30 | ->setDescription('Allows for quick SSH into a service container.') 31 | ->addArgument( 32 | 'service', 33 | InputArgument::REQUIRED, 34 | 'Service to SSH into the container of: http, php, db, redis, solr, memcache'); 35 | } 36 | 37 | /** 38 | * {@inheritdoc} 39 | */ 40 | protected function execute(InputInterface $input, OutputInterface $output) 41 | { 42 | $containerName = null; 43 | $type = $input->getArgument('service'); 44 | 45 | $containerNameMap = [ 46 | 'http' => 'nginx', 47 | 'php' => 'phpfpm', 48 | 'db' => 'mariadb', 49 | 'redis' => 'redis', 50 | 'solr' => 'solr', 51 | 'memcache' => 'memcached', 52 | 'blackfire' => 'blackfire', 53 | ]; 54 | 55 | if (!isset($containerNameMap[$type])) { 56 | $this->stdOut->writeln("Invalid service type"); 57 | return 1; 58 | } else { 59 | $containerName = $containerNameMap[$type]; 60 | } 61 | 62 | $builder = ProcessBuilder::create([ 63 | 'docker', 64 | 'exec', 65 | '-it', 66 | Compose::getContainerName(Platform::projectName(), $containerName), 67 | 'bash' 68 | ]); 69 | 70 | $process = $builder->getProcess(); 71 | // Need to set tty true, ProccessHelper doesn't allow this setting. 72 | $process->setTty(true); 73 | try { 74 | $process->mustRun(null); 75 | } catch(ProcessFailedException $e) { 76 | $message = "The command failed with the exit code: " . $process->getExitCode(); 77 | $message .= "\n\nFull command: " . $process->getCommandLine(); 78 | throw new \Exception($message); 79 | } 80 | 81 | return $process->isSuccessful(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Command/Docker/StopCommand.php: -------------------------------------------------------------------------------- 1 | setName('docker:stop') 24 | ->setAliases(['stop']) 25 | ->setDescription('Stops the docker containers'); 26 | } 27 | 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | protected function execute(InputInterface $input, OutputInterface $output) 32 | { 33 | Compose::stop(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Command/Docker/UpCommand.php: -------------------------------------------------------------------------------- 1 | setName('docker:up') 24 | ->setAliases(['start']) 25 | ->setDescription('Starts the docker containers'); 26 | } 27 | 28 | /** 29 | * {@inheritdoc} 30 | */ 31 | protected function execute(InputInterface $input, OutputInterface $output) 32 | { 33 | $this->stdOut->writeln("Bring up containers"); 34 | Compose::up(['-d']); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Command/DrushCommand.php: -------------------------------------------------------------------------------- 1 | setName('drush') 28 | ->addArgument('cmd', InputArgument::IS_ARRAY | InputArgument::OPTIONAL, 'Command and arguments to pass to Drush', ['status']) 29 | ->setDescription('Runs a Drush command for environment.') 30 | ->setHelp('For example, drush en --drush_option=-y contact') 31 | ->addOption('drush_option', null, InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Additional options to pass to Drush. For example --drush_option=-y'); 32 | 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | * 38 | * @see PlatformCommand::getCurrentEnvironment() 39 | */ 40 | protected function execute(InputInterface $input, OutputInterface $output) 41 | { 42 | $processBuilder = ProcessBuilder::create([ 43 | 'drush', 44 | '--root=' . Platform::webDir(), 45 | '--uri=' . Platform::getUri() 46 | ]); 47 | foreach ($input->getArgument('cmd') as $argument) { 48 | $processBuilder->add($argument); 49 | } 50 | foreach ($input->getOption('drush_option') as $option) { 51 | $processBuilder->add($option); 52 | } 53 | 54 | if ($output->getVerbosity() > $output::VERBOSITY_NORMAL) { 55 | $output->writeln('Running drush command: ' . $processBuilder->getProcess()->getCommandLine() . ''); 56 | } 57 | 58 | passthru($processBuilder->getProcess()->getCommandLine()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Command/Flamegraph/CreateCommand.php: -------------------------------------------------------------------------------- 1 | setName('flamegraph:create') 26 | ->addArgument('filename', InputArgument::REQUIRED) 27 | ->setDescription('Creates a flamegraph from xhprof folder contents.'); 28 | } 29 | 30 | /** 31 | * {@inheritdoc} 32 | * 33 | * @see PlatformCommand::getCurrentEnvironment() 34 | */ 35 | protected function execute(InputInterface $input, OutputInterface $output) 36 | { 37 | $xhpfg = Platform::rootDir() . '/docker/xhpfg/xhprof-sample-to-flamegraph-stacks'; 38 | $fg = Platform::rootDir() . '/docker/fg/flamegraph.pl'; 39 | $xhprof = Platform::rootDir() . '/xhprof'; 40 | $graphName = $input->getArgument('filename'); 41 | $graphDestination = Platform::rootDir() . '/' . $graphName . '.svg'; 42 | 43 | exec("$xhpfg $xhprof | $fg > $graphDestination"); 44 | 45 | $url = 'file://' . $graphDestination; 46 | $this->openUrl($url); 47 | } 48 | 49 | /** 50 | * Open a URL in the browser, or print it. 51 | * 52 | * @param string $url 53 | */ 54 | protected function openUrl($url) 55 | { 56 | $browser = $this->getDefaultBrowser(); 57 | if ($browser) { 58 | $opened = $this->getHelper('process')->run($this->stdOut, array($browser, $url)); 59 | if ($opened) { 60 | $this->stdErr->writeln("Opened: $url"); 61 | return; 62 | } 63 | } else { 64 | $this->stdErr->writeln("Browser not found: $browser"); 65 | } 66 | $this->stdOut->writeln($url); 67 | } 68 | 69 | /** 70 | * Find a default browser to use. 71 | * 72 | * @return string|false 73 | */ 74 | protected function getDefaultBrowser() 75 | { 76 | $potential = array('xdg-open', 'open', 'start'); 77 | /** @var \Symfony\Component\Console\Helper\ProcessHelper $process */ 78 | $process = $this->getHelper('process'); 79 | foreach ($potential as $browser) { 80 | // Check if command exists by executing help flag. 81 | 82 | if ($process->run($this->stdOut, "command -v $browser")->isSuccessful()) { 83 | return $browser; 84 | } 85 | } 86 | return false; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Command/Flamegraph/SetupCommand.php: -------------------------------------------------------------------------------- 1 | setName('flamegraph:setup') 28 | ->setDescription('Sets the project up for generating flamegrapghs.'); 29 | } 30 | 31 | /** 32 | * {@inheritdoc} 33 | * 34 | * @see PlatformCommand::getCurrentEnvironment() 35 | */ 36 | protected function execute(InputInterface $input, OutputInterface $output) 37 | { 38 | /** @var \Symfony\Component\Console\Helper\ProcessHelper $process */ 39 | $process = $this->getHelper('process'); 40 | $output->setVerbosity(OutputInterface::VERBOSITY_VERY_VERBOSE); 41 | if (!is_dir(Platform::rootDir() . '/docker/fg')) { 42 | $process->mustRun($output, [ 43 | 'git', 44 | 'clone', 45 | 'https://github.com/brendangregg/FlameGraph.git', 46 | Platform::rootDir() . '/docker/fg' 47 | ]); 48 | } 49 | if (!is_dir(Platform::rootDir() . '/docker/xhpfg')) { 50 | $process->mustRun($output, [ 51 | 'git', 52 | 'clone', 53 | 'https://github.com/msonnabaum/xhprof-flamegraphs.git', 54 | Platform::rootDir() . '/docker/xhpfg' 55 | ]); 56 | } 57 | 58 | $stack = StacksFactory::getStack(Platform::webDir()); 59 | switch ($stack->type()) { 60 | case Stacks\Drupal::TYPE: 61 | $this->stdOut->writeln("Patching Drupal for xhprof"); 62 | $patchProcess = new Process( 63 | 'patch -p1 < ' . CLI_ROOT . '/resources/drupal-enable-profiling.patch', 64 | Platform::webDir() 65 | ); 66 | break; 67 | case Stacks\WordPress::TYPE: 68 | $this->stdOut->writeln("Patching WordPress for xhprof"); 69 | $patchProcess = new Process( 70 | 'patch -p0 < ' . CLI_ROOT . '/resources/wordpress-enable-profiling.patch', 71 | Platform::webDir() 72 | ); 73 | break; 74 | default: 75 | throw new \Exception('Stack type not supported yet.'); 76 | } 77 | $patchProcess->mustRun(); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Command/Flamegraph/UnpatchCommand.php: -------------------------------------------------------------------------------- 1 | setName('flamegraph:unpatch') 28 | ->setDescription('Unpatches index.php to stop xhprof logging.'); 29 | } 30 | 31 | /** 32 | * {@inheritdoc} 33 | * 34 | * @see PlatformCommand::getCurrentEnvironment() 35 | */ 36 | protected function execute(InputInterface $input, OutputInterface $output) 37 | { 38 | $stack = StacksFactory::getStack(Platform::webDir()); 39 | switch ($stack->type()) { 40 | case Stacks\Drupal::TYPE: 41 | $this->stdOut->writeln("Removing patch on Drupal for xhprof"); 42 | $patchProcess = new Process( 43 | 'patch -p1 -R < ' . CLI_ROOT . '/resources/drupal-enable-profiling.patch', 44 | Platform::webDir() 45 | ); 46 | break; 47 | case Stacks\WordPress::TYPE: 48 | $this->stdOut->writeln("Removing patch on WordPress for xhprof"); 49 | $patchProcess = new Process( 50 | 'patch -p0 -R < ' . CLI_ROOT . '/resources/wordpress-enable-profiling.patch', 51 | Platform::webDir() 52 | ); 53 | break; 54 | default: 55 | throw new \Exception('Stack type not supported yet.'); 56 | } 57 | $patchProcess->mustRun(null); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Command/InitCommand.php: -------------------------------------------------------------------------------- 1 | setName('init') 29 | ->setDescription('Setup Platform and Docker Compose files'); 30 | } 31 | /** 32 | * @inheritdoc 33 | */ 34 | protected function initialize(InputInterface $input, OutputInterface $output) { 35 | if (empty(Config::get())) { 36 | $this->cwd = getcwd(); 37 | 38 | /** @var QuestionHelper $helper */ 39 | $helper = $this->getHelper('question'); 40 | $output->writeln("Current directory: {$this->cwd}"); 41 | $question = new ConfirmationQuestion("There isn't a project here, create one? [Y,n] "); 42 | 43 | if ($helper->ask($input, $output, $question)) { 44 | Config::set('alias-group', basename($this->cwd)); 45 | Config::set('name', basename($this->cwd)); 46 | Config::set('path', $this->cwd); 47 | 48 | // Platform.sh scaffold docroot. 49 | if (is_dir($this->cwd . '/' . Platform::DEFAULT_WEB_ROOT)) { 50 | Config::set('docroot', Platform::DEFAULT_WEB_ROOT); 51 | } 52 | // Typical app location. 53 | elseif (is_dir($this->cwd . '/web')) { 54 | Config::set('docroot', 'web'); 55 | } 56 | // Acquia. 57 | elseif (is_dir($this->cwd . '/docroot')) { 58 | Config::set('docroot', 'docroot'); 59 | } 60 | else { 61 | $question = new Question("What is the document root for the project?"); 62 | $answer = $helper->ask($input, $output, $question); 63 | Config::set('docroot', $answer); 64 | } 65 | 66 | if (!Config::write($this->cwd)) { 67 | throw new \Exception('There was an error writing the platform configuration.'); 68 | } 69 | } 70 | else { 71 | exit(1); 72 | } 73 | } 74 | 75 | parent::initialize($input, $output); 76 | } 77 | 78 | 79 | /** 80 | * {@inheritdoc} 81 | */ 82 | protected function execute(InputInterface $input, OutputInterface $output) 83 | { 84 | // If the docker-compose.yml file exists, then start containers. 85 | if (file_exists($this->cwd . '/docker-compose.yml')) { 86 | $this->stdOut->writeln("Docker compose initiated, starting containers. Run docker:rebuild to rebuild."); 87 | return $this->getApplication()->find('docker:up')->run($input, $output); 88 | } 89 | 90 | return $this->getApplication()->find('docker:rebuild')->run($input, $output); 91 | } 92 | 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/Command/LinkCommand.php: -------------------------------------------------------------------------------- 1 | setName('link') 30 | ->setDescription('Displays link to local environment, with port.'); 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | * 36 | * @see PlatformCommand::getCurrentEnvironment() 37 | */ 38 | protected function execute(InputInterface $input, OutputInterface $output) 39 | { 40 | try { 41 | $url = Platform::getUri(); 42 | } catch (\Exception $e) { 43 | $output->writeln('The nginx container is not running.'); 44 | } 45 | 46 | try { 47 | // See if the nginx-proxy is running and use that if it is. 48 | $process = Docker::inspect(['--format="{{ .State.Running }}"', 'nginx-proxy'], true); 49 | if (strpos($process->getOutput(), 'true') !== FALSE) { 50 | $url = 'http://' . Platform::projectName() . '.' . Platform::projectTld(); 51 | } 52 | } 53 | catch (\Exception $e) {} 54 | 55 | if ($url) { 56 | $this->openUrl($url, $this->stdErr, $this->stdOut); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Command/ListCommand.php: -------------------------------------------------------------------------------- 1 | getOption('xml')) { 16 | @trigger_error('The --xml option was deprecated in version 2.7 and will be removed in version 3.0. Use the --format option instead.', E_USER_DEPRECATED); 17 | 18 | $input->setOption('format', 'xml'); 19 | } 20 | 21 | $helper = new DescriptorHelper(); 22 | $helper->register('txt', new CustomTextDescriptor()); 23 | $helper->describe($output, $this->getApplication(), 24 | array( 25 | 'format' => $input->getOption('format'), 26 | 'raw_text' => $input->getOption('raw'), 27 | 'namespace' => $input->getArgument('namespace'), 28 | ) 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Command/Project/BehatCommand.php: -------------------------------------------------------------------------------- 1 | setName('project:behat') 34 | ->setAliases(['behat']) 35 | ->addOption('depth', 'd', InputOption::VALUE_OPTIONAL, 'Directory depth level to search', 5) 36 | ->addOption('folder', 'f', InputOption::VALUE_OPTIONAL, 'Additional folder to scan') 37 | ->setDescription('Runs behat test suite for project. Checks ./tests, ./www, ./shared and ./repository by default.'); 38 | } 39 | 40 | /** 41 | * @inheritdoc 42 | */ 43 | protected function initialize(InputInterface $input, OutputInterface $output) { 44 | parent::initialize($input, $output); 45 | 46 | $behatYmls = []; 47 | foreach ($this->discoverBehatYml() as $file) { 48 | /* @var $file \Symfony\Component\Finder\SplFileInfo */ 49 | $behatYmls[] = $file->getRealPath(); 50 | } 51 | if (count($behatYmls) == 0) { 52 | $output->writeln('Unable to find behat.yml in project'); 53 | exit(1); 54 | } 55 | 56 | $helper = $this->getHelper('question'); 57 | $question = new ChoiceQuestion( 58 | 'Select the Behat configuration to use', 59 | $behatYmls, 60 | 0 61 | ); 62 | $this->behatLocation = $helper->ask($input, $output, $question); 63 | $output->writeln("Using behat.yml: {$this->behatLocation}"); 64 | } 65 | 66 | /** 67 | * {@inheritdoc} 68 | * 69 | * @see PlatformCommand::getCurrentEnvironment() 70 | */ 71 | protected function execute(InputInterface $input, OutputInterface $output) 72 | { 73 | $now = new \DateTime(); 74 | // Hopefully this isn't too opinionated? 75 | $workingDir = dirname($this->behatLocation); 76 | 77 | if (!is_dir($workingDir . '/vendor')) { 78 | $output->writeln("Assumed behat directory doesn't have depednencies installed: $workingDir."); 79 | exit(1); 80 | } 81 | 82 | // Try to find the Behat binary 83 | $composerData = json_decode(file_get_contents($workingDir . '/composer.json'), true); 84 | if (isset($composerData['config']['bin-dir'])) { 85 | $binDir = $composerData['config']['bin-dir']; 86 | } else { 87 | $binDir = 'vendor/bin/'; 88 | } 89 | 90 | $outPath = Platform::rootDir() . '/behat-run-' . $now->getTimestamp() . '.log'; 91 | 92 | $builder = ProcessBuilder::create([ 93 | $binDir . 'behat', 94 | '--format=pretty', 95 | '--out=' . $outPath, 96 | ]); 97 | $builder->setWorkingDirectory($workingDir); 98 | $builder->setTimeout(null); 99 | $process = $builder->getProcess(); 100 | 101 | // @todo: export environment variables (postponed.) 102 | // @note there's also v2 v3 issues we'd have to sort out for exporting. 103 | // just run the dang thing. 104 | 105 | $output->writeln("Running Behat, saving output to $outPath"); 106 | $this->startSeleniumContainer(); 107 | $output->writeln("Behat is running..."); 108 | $process->run(); 109 | if ($process->getExitCode() > 0) { 110 | $this->stdOut->writeln("Behat tests had failure."); 111 | /** @var \Symfony\Component\Console\Helper\ProcessHelper $process */ 112 | $process = $this->getHelper('process'); 113 | $potential = array('xdg-open', 'open', 'start'); 114 | foreach ($potential as $app) { 115 | // Check if command exists by executing help flag. 116 | 117 | if ($process->run($this->stdOut, "command -v $app")->isSuccessful()) { 118 | $process->run($this->stdOut, array($app, $outPath)); 119 | } 120 | } 121 | } 122 | $this->stopSeleniumContainer(); 123 | } 124 | 125 | protected function discoverBehatYml() 126 | { 127 | $depth = $this->stdIn->getOption('depth'); 128 | $scanDirs = [ 129 | Platform::sharedDir(), 130 | Platform::webDir(), 131 | ]; 132 | 133 | if (is_dir(Platform::repoDir())) { 134 | $scanDirs[] = Platform::repoDir(); 135 | } 136 | 137 | if (is_dir(Platform::testsDir())) { 138 | $scanDirs[] = Platform::testsDir(); 139 | } 140 | 141 | $extraDir = $this->stdIn->getOption('folder'); 142 | if ($extraDir && is_dir($extraDir)) { 143 | $scanDirs[] = $extraDir; 144 | } 145 | 146 | $finder = new Finder(); 147 | $finder->files() 148 | ->in($scanDirs) 149 | ->depth("< $depth") 150 | ->name('behat.yml'); 151 | return $finder; 152 | } 153 | 154 | protected function startSeleniumContainer() 155 | { 156 | $this->stdOut->writeln("Starting the Selenium container"); 157 | try { 158 | return !Docker::start([$this->containerName])->isSuccessful(); 159 | } catch (\Exception $e) { 160 | $this->stdOut->writeln("Creating the Selenium container"); 161 | return Docker::run([ 162 | '-d', 163 | '-p', 164 | '4444:4444', 165 | '-v', 166 | '/dev/shm:/dev/shm', 167 | '--name', 168 | $this->containerName, 169 | 'selenium/standalone-firefox:2.47.1', 170 | ]); 171 | } 172 | } 173 | 174 | protected function stopSeleniumContainer() 175 | { 176 | $this->stdOut->writeln("Stopping the Selenium container"); 177 | return Docker::stop([$this->containerName]); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/Command/Project/DbSyncCommand.php: -------------------------------------------------------------------------------- 1 | setName('project:db-sync') 28 | ->addArgument('file', InputArgument::OPTIONAL, 'File path of SQL dump to import, defaults to ../dump.sql', '../dump.sql') 29 | ->setDescription('Syncs database from environment to local'); 30 | } 31 | 32 | /** 33 | * {@inheritdoc} 34 | * 35 | * @see PlatformCommand::getCurrentEnvironment() 36 | */ 37 | protected function execute(InputInterface $input, OutputInterface $output) 38 | { 39 | $this->stdOut->writeln("Syncing Platform.sh environment database to local"); 40 | 41 | /** @var \Symfony\Component\Console\Helper\ProcessHelper $process */ 42 | $process = $this->getHelper('process'); 43 | 44 | // If this is a Platform.sh project, get latest dump. 45 | // @todo: add proper provider integration 46 | if (Config::get('id')) { 47 | $process->mustRun($this->stdOut, [ 48 | 'platform', 49 | 'sql-dump', 50 | ]); 51 | } 52 | 53 | $cd = getcwd(); 54 | chdir(Platform::webDir()); 55 | $process->run($this->stdOut, 'drush sqlc < ' . $input->getArgument('file')); 56 | chdir($cd); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Command/Providers/Commerce2xProviderCommand.php: -------------------------------------------------------------------------------- 1 | getArgument('project'), 38 | '--stability', 39 | 'dev' 40 | ]); 41 | $builder->setTimeout(null); 42 | $builder->enableOutput(); 43 | $process = $builder->getProcess(); 44 | $process->run(); 45 | 46 | if (!$process->isSuccessful()) { 47 | $output->writeln($process->getErrorOutput()); 48 | } else { 49 | $output->writeln($process->getOutput()); 50 | } 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Command/Providers/PlatformshProviderCommand.php: -------------------------------------------------------------------------------- 1 | getArgument('project'), 29 | '--yes' 30 | ]); 31 | $builder->setTimeout(null); 32 | $builder->enableOutput(); 33 | $process = $builder->getProcess(); 34 | $process->run(); 35 | 36 | if (!$process->isSuccessful()) { 37 | $output->writeln($process->getErrorOutput()); 38 | } 39 | else { 40 | $buildDirectory = ''; 41 | $platformOutput = $process->getOutput(); 42 | foreach (explode(PHP_EOL, $platformOutput) as $input_line) { 43 | preg_match("/^ @(.*)._local$/", $input_line, $output_array); 44 | if (isset($output_array[1])) { 45 | $buildDirectory = $output_array[1]; 46 | $output->writeln("Folder name is {$output_array[1]}"); 47 | } 48 | } 49 | $this->runBuild($buildDirectory); 50 | } 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/Command/Providers/ProviderCommand.php: -------------------------------------------------------------------------------- 1 | stdOut = $output; 27 | $this->stdErr = $output instanceof ConsoleOutputInterface ? $output->getErrorOutput() : $output; 28 | $this->stdIn = $input; 29 | self::$interactive = $input->isInteractive(); 30 | } 31 | 32 | 33 | abstract function providerCommandName(); 34 | abstract function providerName(); 35 | 36 | /** 37 | * {@inheritdoc} 38 | */ 39 | protected function configure() 40 | { 41 | $this 42 | ->setName('provider:' . $this->providerCommandName()) 43 | ->setAliases([$this->providerCommandName()]) 44 | ->addArgument('project', InputArgument::REQUIRED, 'Project identifier') 45 | ->setDescription("Sets up a {$this->providerName()} project"); 46 | 47 | } 48 | 49 | protected function runBuild($buildDirectory) { 50 | // $fakeInput = new ArgvInput(); 51 | // return $this->getApplication()->find('docker:rebuild')->run($fakeInput, $output); 52 | chdir($buildDirectory); 53 | passthru('platform-docker docker:rebuild'); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Config.php: -------------------------------------------------------------------------------- 1 | setConfig($key, $value); 27 | } 28 | 29 | public static function write($destinationDir = null) 30 | { 31 | if (!$destinationDir) { 32 | $destinationDir = Platform::rootDir(); 33 | } 34 | return self::instance()->writeConfig($destinationDir); 35 | } 36 | 37 | public function setConfig($key, $value) 38 | { 39 | $this->config[$key] = $value; 40 | return $this; 41 | } 42 | 43 | public function writeConfig($destinationDir = null) 44 | { 45 | if (!$destinationDir) { 46 | $destinationDir = Platform::rootDir(); 47 | } 48 | return file_put_contents($destinationDir . '/' . self::PLATFORM_CONFIG, Yaml::dump($this->config, 2)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/Descriptor/CustomTextDescriptor.php: -------------------------------------------------------------------------------- 1 | getColumnWidth($description->getCommands()); 22 | 23 | foreach ($description->getCommands() as $command) { 24 | $this->writeText(sprintf("%-${width}s %s", $command->getName(), $command->getDescription()), $options); 25 | $this->writeText("\n"); 26 | } 27 | } else { 28 | if ('' != $help = $application->getHelp()) { 29 | $this->writeText("$help\n\n", $options); 30 | } 31 | 32 | $this->describeInputDefinition(new InputDefinition($application->getDefinition()->getOptions()), $options); 33 | 34 | $this->writeText("\n"); 35 | $this->writeText("\n"); 36 | 37 | $width = $this->getColumnWidth($description->getCommands()); 38 | 39 | if ($describedNamespace) { 40 | $this->writeText(sprintf('Available commands for the "%s" namespace:', $describedNamespace), $options); 41 | } else { 42 | $this->writeText('Available commands:', $options); 43 | } 44 | 45 | // add commands by namespace 46 | foreach ($description->getNamespaces() as $namespace) { 47 | if (!$describedNamespace && ApplicationDescription::GLOBAL_NAMESPACE !== $namespace['id']) { 48 | $this->writeText("\n"); 49 | $this->writeText(' '.$namespace['id'].'', $options); 50 | } 51 | 52 | foreach ($namespace['commands'] as $name) { 53 | $command = $description->getCommand($name); 54 | $aliases = $command->getAliases(); 55 | if ($aliases && in_array($name, $aliases)) { 56 | // skip aliases 57 | continue; 58 | } 59 | 60 | $this->writeText("\n"); 61 | $this->writeText( 62 | sprintf( 63 | " %-${width}s %s", 64 | "$name" . $this->formatAliases($aliases), 65 | $command->getDescription() 66 | ), 67 | $options 68 | ); 69 | } 70 | } 71 | 72 | $this->writeText("\n"); 73 | } 74 | } 75 | 76 | /** 77 | * @param array $aliases 78 | * 79 | * @return string 80 | */ 81 | protected function formatAliases(array $aliases) 82 | { 83 | return $aliases ? " (" . implode(', ', $aliases) . ")" : ''; 84 | } 85 | 86 | 87 | 88 | /** 89 | * {@inheritdoc} 90 | */ 91 | private function writeText($content, array $options = array()) 92 | { 93 | $this->write( 94 | isset($options['raw_text']) && $options['raw_text'] ? strip_tags($content) : $content, 95 | isset($options['raw_output']) ? !$options['raw_output'] : true 96 | ); 97 | } 98 | 99 | /** 100 | * @param \Symfony\Component\Console\Command\Command[] $commands 101 | * 102 | * @return int 103 | */ 104 | protected function getColumnWidth(array $commands) 105 | { 106 | $width = 0; 107 | foreach ($commands as $command) { 108 | $aliasesString = $this->formatAliases($command->getAliases()); 109 | $commandWidth = strlen($command->getName()) + strlen($aliasesString); 110 | $width = $commandWidth > $width ? $commandWidth : $width; 111 | } 112 | // Add the indent. 113 | $width += 2; 114 | // Accommodate tags. 115 | $width += strlen(''); 116 | return $width; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Docker/ComposeConfig.php: -------------------------------------------------------------------------------- 1 | resourcesDir = CLI_ROOT . '/resources'; 50 | $this->projectPath = Platform::rootDir(); 51 | $this->fs = new Filesystem(); 52 | $this->phpVersion = $phpVersion ?: '5.6'; 53 | } 54 | 55 | public function writeDockerCompose(ComposeContainers $composeContainers) 56 | { 57 | $this->fs->dumpFile($this->projectPath . '/docker-compose.yml', $composeContainers->yaml()); 58 | } 59 | 60 | /** 61 | * 62 | */ 63 | public function ensureDirectories() 64 | { 65 | $this->fs->mkdir([ 66 | $this->projectPath . '/xhprof', 67 | $this->projectPath . '/docker/data', 68 | $this->projectPath . '/docker/conf', 69 | $this->projectPath . '/docker/conf/solr', 70 | $this->projectPath . '/docker/images', 71 | $this->projectPath . '/docker/ssl' 72 | ]); 73 | } 74 | 75 | public function copyImages() 76 | { 77 | // Copy Dockerfile for php-fpm 78 | $this->fs->copy($this->resourcesDir . "/images/php/{$this->phpVersion}/Dockerfile", 79 | $this->projectPath . '/docker/images/php/Dockerfile', TRUE); 80 | } 81 | 82 | public function copyConfigs() 83 | { 84 | // Copy configs 85 | foreach ($this->configsToCopy() as $source => $fileName) { 86 | $this->fs->copy($this->resourcesDir . '/conf/' . $source, 87 | $this->projectPath . '/docker/conf/' . $fileName, TRUE); 88 | } 89 | 90 | // Change the default xdebug remote host when using Docker Machine 91 | if (!Docker::native()) { 92 | $phpConfFile = $this->projectPath . '/docker/conf/php.ini'; 93 | $phpConf = file_get_contents($phpConfFile); 94 | $phpConf = str_replace('172.17.42.1', '192.168.99.1', $phpConf); 95 | file_put_contents($phpConfFile, $phpConf); 96 | } 97 | // Change xdebug remote host for Windows and Mac beta 98 | // @todo No idea if this IP matches on Windows. 99 | elseif (PHP_OS != 'Linux') { 100 | $phpConfFile = $this->projectPath . '/docker/conf/php.ini'; 101 | $phpConf = file_get_contents($phpConfFile); 102 | $phpConf = str_replace('172.17.42.1', '192.168.65.1', $phpConf); 103 | file_put_contents($phpConfFile, $phpConf); 104 | } 105 | 106 | // Quick fix to make nginx PHP_IDE_CONFIG dynamic for now. 107 | $nginxConfFile= $this->projectPath . '/docker/conf/nginx.conf'; 108 | $nginxConf = file_get_contents($nginxConfFile); 109 | $nginxConf = str_replace('{{ platform }}', Platform::projectName() . '.' . Platform::projectTld(), $nginxConf); 110 | $nginxConf = str_replace('{{ docroot }}', Config::get('docroot'), $nginxConf); 111 | file_put_contents($nginxConfFile, $nginxConf); 112 | 113 | if (PlatformAppConfig::hasSolr()) { 114 | // stub in for Solr configs 115 | $solr_major_version = PlatformServiceConfig::getSolrMajorVersion(); 116 | $finder = new Finder(); 117 | $finder->in($this->resourcesDir . "/conf/solr/$solr_major_version.x/") 118 | ->files() 119 | ->depth('< 1') 120 | ->name('*'); 121 | /** @var \SplFileInfo $file */ 122 | foreach ($finder as $file) { 123 | $this->fs->copy($file->getPathname(), $this->projectPath . '/docker/conf/solr/' . $file->getFilename(), TRUE); 124 | } 125 | } 126 | 127 | // copy ssl 128 | $this->fs->copy($this->resourcesDir . '/ssl/nginx.crt', $this->projectPath . '/docker/ssl/nginx.crt', TRUE); 129 | $this->fs->copy($this->resourcesDir . '/ssl/nginx.key', $this->projectPath . '/docker/ssl/nginx.key', TRUE); 130 | } 131 | 132 | /** 133 | * @return array 134 | */ 135 | protected function configsToCopy() 136 | { 137 | return [ 138 | "php/{$this->phpVersion}/fpm.conf" => 'fpm.conf', 139 | 'mysql.cnf' => 'mysql.cnf', 140 | 'nginx.conf' => 'nginx.conf', 141 | "php/{$this->phpVersion}/php.ini" => 'php.ini', 142 | ]; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/Docker/ComposeContainers.php: -------------------------------------------------------------------------------- 1 | path = $path; 39 | $this->name = $name; 40 | // Add required containers. 41 | $this->addPhpFpm(); 42 | $this->addDatabase(); 43 | $this->addWebserver(); 44 | } 45 | 46 | 47 | /** 48 | * @return string 49 | */ 50 | public function yaml() { 51 | return Yaml::dump($this->config); 52 | } 53 | 54 | /** 55 | * 56 | */ 57 | public function addPhpFpm() 58 | { 59 | $this->config['phpfpm'] = [ 60 | 'command' => 'php-fpm --allow-to-run-as-root', 61 | 'build' => 'docker/images/php', 62 | 'volumes' => [ 63 | './docker/conf/fpm.conf:/usr/local/etc/php-fpm.conf', 64 | './:/var/platform:cached', 65 | './docker/conf/php.ini:/usr/local/etc/php/conf.d/local.ini', 66 | ], 67 | 'links' => [ 68 | 'mariadb', 69 | ], 70 | 'environment' => [ 71 | 'PLATFORM_DOCKER' => $this->name, 72 | 'PHP_IDE_CONFIG' => 'serverName=' . $this->name . '.' . Platform::projectTld(), 73 | ], 74 | ]; 75 | } 76 | 77 | /** 78 | * 79 | */ 80 | public function addDatabase() 81 | { 82 | $this->config['mariadb'] = [ 83 | // @todo if comman run with verbose, tag verbose. 84 | 'command' => 'mysqld --user=root --verbose', 85 | 'image' => 'mariadb', 86 | 'ports' => [ 87 | '3306', 88 | ], 89 | 'volumes' => [ 90 | './docker/data:/var/lib/mysql', 91 | './docker/conf/mysql.cnf:/etc/mysql/my.cnf', 92 | ], 93 | 'environment' => [ 94 | 'MYSQL_DATABASE' => 'data', 95 | 'MYSQL_USER' => 'mysql', 96 | 'MYSQL_PASSWORD' => 'mysql', 97 | 'MYSQL_ALLOW_EMPTY_PASSWORD' => 'yes', 98 | 'MYSQL_ROOT_PASSWORD' => 'root,' 99 | ], 100 | ]; 101 | } 102 | 103 | /** 104 | * 105 | */ 106 | public function addWebserver() 107 | { 108 | $this->config['nginx'] = [ 109 | 'image' => 'nginx:1.9.0', 110 | 'volumes' => [ 111 | './docker/conf/nginx.conf:/etc/nginx/conf.d/default.conf', 112 | './:/var/platform:cached', 113 | './docker/ssl/nginx.crt:/etc/nginx/ssl/nginx.crt', 114 | './docker/ssl/nginx.key:/etc/nginx/ssl/nginx.key', 115 | ], 116 | 'ports' => [ 117 | '80', 118 | '443', 119 | ], 120 | 'links' => [ 121 | 'phpfpm', 122 | ], 123 | 'environment' => [ 124 | 'VIRTUAL_HOST' => $this->name . '.' . Platform::projectTld(), 125 | 'PLATFORM_DOCKER' => $this->name, 126 | ], 127 | ]; 128 | } 129 | 130 | /** 131 | * 132 | */ 133 | public function addRedis() { 134 | $this->config['redis'] = [ 135 | 'image' => 'redis', 136 | 'ports' => [ 137 | '6379', 138 | ], 139 | ]; 140 | $this->config['phpfpm']['links'][] = 'redis'; 141 | } 142 | 143 | public function addSolr() 144 | { 145 | $solr_type = PlatformServiceConfig::getSolrType(); 146 | switch ($solr_type) { 147 | case 'solr:6.3': 148 | $image = $solr_type; 149 | break; 150 | default: 151 | $image = 'makuk66/docker-solr:4.10.4'; 152 | } 153 | switch (PlatformServiceConfig::getSolrMajorVersion()) { 154 | case '6': 155 | $solr_volume = './docker/conf/solr:/opt/solr/server/solr/mycores/conf'; 156 | break; 157 | default: 158 | $solr_volume = './docker/conf/solr:/opt/solr/example/solr/collection1/conf'; 159 | } 160 | $this->config['solr'] = [ 161 | 'image' => $image, 162 | 'ports' => [ 163 | '8983', 164 | ], 165 | 'volumes' => [ 166 | $solr_volume, 167 | ], 168 | ]; 169 | $this->config['phpfpm']['links'][] = 'solr'; 170 | $this->config['nginx']['links'][] = 'solr'; 171 | } 172 | 173 | public function addMemcached() { 174 | $this->config['memcached'] = [ 175 | 'image' => 'memcached', 176 | ]; 177 | $this->config['phpfpm']['links'][] = 'memcached'; 178 | } 179 | 180 | public function addBlackfire() { 181 | $this->config['blackfire'] = [ 182 | 'image' => 'blackfire/blackfire', 183 | 'ports' => [ 184 | '8707', 185 | ], 186 | 'environment' => [ 187 | 'BLACKFIRE_SERVER_ID' => Config::get('blackfire_server_id'), 188 | 'BLACKFIRE_SERVER_TOKEN' => Config::get('blackfire_server_token'), 189 | 'BLACKFIRE_LOG_LEVEL' => 4 190 | ], 191 | ]; 192 | $this->config['phpfpm']['links'][] = 'blackfire'; 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/Platform.php: -------------------------------------------------------------------------------- 1 | get('type'), 2); 26 | if (strcasecmp($app, 'php') === 0) { 27 | return $version; 28 | } 29 | return NULL; 30 | } 31 | 32 | /** 33 | * Determines if the site is using redis. 34 | * 35 | * @return bool 36 | */ 37 | public static function hasRedis() { 38 | $relationships = self::get('relationships'); 39 | return isset($relationships['redis']); 40 | } 41 | 42 | /** 43 | * Determines if the site is using solr. 44 | * 45 | * @return bool 46 | */ 47 | public static function hasSolr() { 48 | $relationships = self::get('relationships'); 49 | return isset($relationships['solr']); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/PlatformServiceConfig.php: -------------------------------------------------------------------------------- 1 | version = $drupalStack->version(Platform::webDir()); 26 | } 27 | 28 | /** 29 | * 30 | */ 31 | public function configure() { 32 | $this->ensureSettings(); 33 | $this->ensureLocalSettings(); 34 | $this->drushrc(); 35 | } 36 | 37 | /** 38 | * Ensures that the Drupal settings.php is available. 39 | * @throws \Exception 40 | */ 41 | protected function ensureSettings() { 42 | if (!$this->fs->exists(Platform::webDir() . '/sites/default')) { 43 | $this->fs->mkdir(Platform::webDir() . '/sites/default', 0775); 44 | } 45 | else { 46 | // If building from an existing project, Drupal may have fiddled with 47 | // the permissions preventing us from writing. 48 | $this->fs->chmod(Platform::webDir() . '/sites/default', 0775); 49 | } 50 | $has_settings = $this->fs->exists(Platform::webDir() . '/sites/default/settings.php'); 51 | if ($has_settings) { 52 | $this->fs->chmod(Platform::webDir() . '/sites/default/settings.php', 0664); 53 | } 54 | 55 | switch ($this->version) { 56 | case DrupalStackHelper::DRUPAL7: 57 | if (!$has_settings) { 58 | $this->fs->copy(CLI_ROOT . '/resources/stacks/drupal7/settings.php', Platform::webDir() . '/sites/default/settings.php', true); 59 | } 60 | break; 61 | case DrupalStackHelper::DRUPAL8: 62 | if (!$has_settings) { 63 | $this->fs->copy(CLI_ROOT . '/resources/stacks/drupal8/settings.php', Platform::webDir() . '/sites/default/settings.php', true); 64 | } 65 | $this->fs->mkdir([ 66 | Platform::sharedDir() . '/config', 67 | Platform::sharedDir() . '/config/active', 68 | Platform::sharedDir() . '/config/staging', 69 | ]); 70 | break; 71 | default: 72 | throw new \Exception('Unsupported version of Drupal. Write a pull reuqest!'); 73 | } 74 | } 75 | 76 | protected function ensureLocalSettings() { 77 | // @todo: Check if settings.local.php exists, load in any $conf changes. 78 | $target_local_settings = Platform::sharedDir() . '/settings.local.php'; 79 | if ($this->fs->exists($target_local_settings)) { 80 | // Try and make it deletable. 81 | $this->fs->chmod($target_local_settings, 0664); 82 | } 83 | switch ($this->version) { 84 | case DrupalStackHelper::DRUPAL7: 85 | $this->fs->copy(CLI_ROOT . '/resources/stacks/drupal7/settings.local.php', $target_local_settings, true); 86 | break; 87 | case DrupalStackHelper::DRUPAL8: 88 | $this->fs->copy(CLI_ROOT . '/resources/stacks/drupal8/settings.local.php', $target_local_settings, true); 89 | break; 90 | default: 91 | throw new \Exception('Unsupported version of Drupal. Write a pull reuqest!'); 92 | } 93 | 94 | // Replace template variables. 95 | $localSettings = file_get_contents($target_local_settings); 96 | $localSettings = str_replace('{{ salt }}', hash('sha256', serialize($_SERVER)), $localSettings); 97 | $localSettings = str_replace('{{ container_name }}', $this->containerName, $localSettings); 98 | $localSettings = str_replace('{{ redis_container_name }}', $this->redisContainerName, $localSettings); 99 | $localSettings = str_replace('{{ solr_container_name }}', $this->solrContainerName, $localSettings); 100 | $localSettings = str_replace('{{ project_domain }}', $this->projectName . '.' . $this->projectTld, $localSettings); 101 | $localSettings = str_replace('{{ project_domain }}', $this->projectName . '.' . $this->projectTld, $localSettings); 102 | file_put_contents($target_local_settings, $localSettings); 103 | 104 | // Relink if missing. 105 | if (!$this->fs->exists(Platform::webDir() . '/sites/default/settings.local.php')) { 106 | $this->fs->symlink($this->getRelativeLinkToShared() . '/settings.local.php', Platform::webDir() . '/sites/default/settings.local.php'); 107 | } 108 | } 109 | 110 | 111 | /** 112 | * Write a drushrc 113 | */ 114 | public function drushrc() { 115 | // @todo: Check if drushrc.php exists, load in any $conf changes. 116 | switch ($this->version) { 117 | case DrupalStackHelper::DRUPAL7: 118 | $this->fs->copy(CLI_ROOT . '/resources/stacks/drupal7/drushrc.php', Platform::sharedDir() . '/drushrc.php', true); 119 | break; 120 | case DrupalStackHelper::DRUPAL8: 121 | $this->fs->copy(CLI_ROOT . '/resources/stacks/drupal8/drushrc.php', Platform::sharedDir() . '/drushrc.php', true); 122 | break; 123 | default: 124 | throw new \Exception('Unsupported version of Drupal. Write a pull reuqest!'); 125 | } 126 | 127 | // Replace template variables. 128 | $localSettings = file_get_contents(Platform::sharedDir() . '/drushrc.php'); 129 | // @todo this expects proxy to be running. 130 | $localSettings = str_replace('{{ project_domain }}', $this->projectName . '.' . $this->projectTld, $localSettings); 131 | file_put_contents(Platform::sharedDir() . '/drushrc.php', $localSettings); 132 | 133 | if (!file_exists(Platform::webDir() . '/sites/default/drushrc.php')) { 134 | $this->fs->symlink($this->getRelativeLinkToShared() . 'drushrc.php', Platform::webDir() . '/sites/default/drushrc.php'); 135 | } 136 | } 137 | 138 | /** 139 | * Gets the relative path from the sites/default directory to the shared directory. 140 | * 141 | * @return string 142 | */ 143 | protected function getRelativeLinkToShared() { 144 | return $this->fs->makePathRelative(realpath(Platform::sharedDir()), realpath(Platform::webDir() . '/sites/default/')); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/Stacks/StackTypeInterface.php: -------------------------------------------------------------------------------- 1 | fs = new Filesystem(); 43 | $this->projectName = Platform::projectName(); 44 | $this->projectTld = Platform::projectTld(); 45 | $this->containerName = Compose::getContainerName(Platform::projectName(), 'mariadb'); 46 | $this->redisContainerName = PlatformAppConfig::hasRedis() ? Compose::getContainerName(Platform::projectName(), 'redis') : ''; 47 | $this->solrContainerName = PlatformAppConfig::hasSolr() ? Compose::getContainerName(Platform::projectName(), 'solr') : ''; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Stacks/StacksFactory.php: -------------------------------------------------------------------------------- 1 | configure(); 18 | break; 19 | case Stacks\WordPress::TYPE: 20 | $wordpress = new WordPress(); 21 | $wordpress->configure(); 22 | break; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/Stacks/WordPress.php: -------------------------------------------------------------------------------- 1 | fs->copy(CLI_ROOT . '/resources/stacks/wordpress/wp-config.php', Platform::webDir() . '/wp-config.php', true); 21 | $this->fs->remove(Platform::webDir() . '/wp-confg-sample.php'); 22 | 23 | $this->fs->copy(CLI_ROOT . '/resources/stacks/wordpress/wp-config.local.php', Platform::sharedDir() . '/wp-config.local.php'); 24 | 25 | $localSettings = file_get_contents(Platform::sharedDir() . '/wp-config.local.php'); 26 | $localSettings = str_replace('{{ salts }}', $this->salts(), $localSettings); 27 | $localSettings = str_replace('{{ container_name }}', $this->containerName, $localSettings); 28 | $localSettings = str_replace('{{ project_domain }}', $this->projectName . '.' . $this->projectTld, $localSettings); 29 | file_put_contents(Platform::sharedDir() . '/wp-config.local.php', $localSettings); 30 | 31 | // Relink if missing. 32 | if (!$this->fs->exists(Platform::webDir() . '/wp-config.local.php')) { 33 | $this->fs->symlink('../shared/wp-config.local.php', Platform::webDir() . '/wp-config.local.php'); 34 | } 35 | } 36 | 37 | /** 38 | * Returns salt defines. 39 | * @return string 40 | */ 41 | public function salts() { 42 | return file_get_contents('https://api.wordpress.org/secret-key/1.1/salt/'); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/YamlConfigReader.php: -------------------------------------------------------------------------------- 1 | getConfigFilePath(); 50 | if ((empty($this->config)) && file_exists($path)) { 51 | $this->config = Yaml::parse(file_get_contents($path)); 52 | } 53 | } 54 | 55 | /** 56 | * The path to the configuration to read. Relative to the Platform root dir. 57 | * 58 | * @return string 59 | */ 60 | abstract protected function getConfigFilePath(); 61 | 62 | /** 63 | * Gets a value from the configuration for the specified key. 64 | * 65 | * @param string|null $key 66 | * 67 | * @return mixed 68 | */ 69 | public static function get($key = null) 70 | { 71 | return self::instance()->getConfig($key); 72 | } 73 | 74 | /** 75 | * Gets a value from the configuration for the specified key. 76 | * 77 | * @param string|null $key 78 | * 79 | * @return mixed 80 | */ 81 | public function getConfig($key = null) 82 | { 83 | if ($key) { 84 | return isset($this->config[$key]) ? $this->config[$key] : null; 85 | } 86 | return $this->config; 87 | } 88 | 89 | /** 90 | * Resets the static instance. 91 | * 92 | * @return object 93 | */ 94 | public static function reset() 95 | { 96 | return self::instance(true); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /stub.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | -------------------------------------------------------------------------------- /tests/Utils/BaseUtilsTest.php: -------------------------------------------------------------------------------- 1 | createTestProject(); 27 | } 28 | 29 | 30 | /** 31 | * {@inheritdoc} 32 | */ 33 | public static function tearDownAfterClass() 34 | { 35 | exec('rm -Rf ' . escapeshellarg(self::$tmpName)); 36 | } 37 | 38 | protected function createTestProject($fixture = '.platform.app.yaml', $services = 'services.yaml') 39 | { 40 | $testDir = self::$tmpName = tempnam(sys_get_temp_dir(), ''); 41 | unlink($testDir); 42 | mkdir($testDir); 43 | mkdir($testDir . '/.platform'); 44 | 45 | file_put_contents($testDir . '/' . Config::PLATFORM_CONFIG, 'name: phpunit'); 46 | copy(__DIR__ .'/../fixtures/' . $fixture, $testDir . '/.platform.app.yaml'); 47 | copy(__DIR__ .'/../fixtures/' . $services, $testDir . '/.platform/services.yaml'); 48 | chdir($testDir); 49 | PlatformAppConfig::reset(); 50 | PlatformServiceConfig::reset(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /tests/Utils/ComposeContainersTest.php: -------------------------------------------------------------------------------- 1 | yaml()); 22 | 23 | $this->assertArrayHasKey('phpfpm', $config_converted); 24 | $this->assertArrayHasKey('nginx', $config_converted); 25 | $this->assertArrayHasKey('mariadb', $config_converted); 26 | $this->assertCount(3, $config_converted); 27 | } 28 | 29 | public function testAddExtras() 30 | { 31 | $config = new ComposeContainers(Platform::rootDir(), Platform::projectName()); 32 | $config->addRedis(); 33 | $config_converted = Yaml::parse($config->yaml()); 34 | $this->assertCount(4, $config_converted); 35 | $config->addSolr(); 36 | $config_converted = Yaml::parse($config->yaml()); 37 | $this->assertCount(5, $config_converted); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /tests/Utils/ConfigTest.php: -------------------------------------------------------------------------------- 1 | assertArrayHasKey('name', Config::get()); 20 | $this->assertEquals('phpunit', Config::get('name')); 21 | } 22 | 23 | public function testSet() 24 | { 25 | Config::set('stack', 'wordpress'); 26 | $this->assertEquals('wordpress', Config::get('stack')); 27 | Config::reset(); 28 | $this->assertArrayNotHasKey('stack', Config::get()); 29 | 30 | Config::set('stack', 'wordpress'); 31 | Config::write(); 32 | Config::reset(); 33 | $this->assertEquals('wordpress', Config::get('stack')); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests/Utils/PlatformAppConfigTest.php: -------------------------------------------------------------------------------- 1 | createTestProject($fixture); 21 | $config = new PlatformAppConfig(); 22 | $this->assertEquals($expected, $config->getPhpVersion()); 23 | } 24 | 25 | public function providerGetPhpVersion() { 26 | return [ 27 | ['.platform.app.yaml', '7.0'], 28 | ['5.6.platform.app.yaml', '5.6'], 29 | ]; 30 | } 31 | 32 | /** 33 | * @dataProvider providerHasService 34 | */ 35 | public function testHasRedis($fixture, $expected) 36 | { 37 | $this->createTestProject($fixture); 38 | $this->assertEquals($expected, PlatformAppConfig::hasSolr()); 39 | } 40 | 41 | /** 42 | * @dataProvider providerHasService 43 | */ 44 | public function testHasSolr($fixture, $expected) 45 | { 46 | $this->createTestProject($fixture); 47 | $this->assertEquals($expected, PlatformAppConfig::hasSolr()); 48 | } 49 | 50 | public function providerHasService() { 51 | return [ 52 | ['.platform.app.yaml', TRUE], 53 | ['5.6.platform.app.yaml', FALSE], 54 | ]; 55 | } 56 | 57 | /** 58 | * {@inheritdoc} 59 | */ 60 | public function tearDown() 61 | { 62 | exec('rm -Rf ' . escapeshellarg(self::$tmpName)); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /tests/Utils/PlatformServiceConfigTest.php: -------------------------------------------------------------------------------- 1 | createTestProject(); 19 | $this->assertEquals('solr:6.3', PlatformServiceConfig::getSolrType()); 20 | } 21 | 22 | /** 23 | * @covers ::getSolrMajorVersion 24 | */ 25 | public function testGetSolrMajorVersion() 26 | { 27 | $this->createTestProject(); 28 | $this->assertEquals('6', PlatformServiceConfig::getSolrMajorVersion()); 29 | 30 | $this->createTestProject('.platform.app.yaml', 'solr-3-services.yaml'); 31 | $this->assertEquals('4', PlatformServiceConfig::getSolrMajorVersion()); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /tests/Utils/PlatformTest.php: -------------------------------------------------------------------------------- 1 | assertEquals('phpunit', Platform::projectName()); 19 | 20 | // Change values 21 | file_put_contents(Platform::rootDir() . '/' . Config::PLATFORM_CONFIG, ''); 22 | Config::reset(); 23 | $this->assertNull(Platform::projectName()); 24 | } 25 | 26 | public function testGetRootDir() 27 | { 28 | $this->assertEquals(self::$tmpName, Platform::rootDir()); 29 | mkdir(self::$tmpName . '/sub'); 30 | mkdir(self::$tmpName . '/sub/sub'); 31 | chdir(self::$tmpName . '/sub/sub'); 32 | $this->assertEquals(self::$tmpName, Platform::rootDir()); 33 | } 34 | 35 | public function testSharedDir() 36 | { 37 | mkdir(Platform::sharedDir(), 0777, TRUE); 38 | $this->assertTrue(is_dir(self::$tmpName . '/.platform/local/shared')); 39 | } 40 | 41 | public function testRepoDir() 42 | { 43 | mkdir(Platform::repoDir()); 44 | $this->assertTrue(is_dir(self::$tmpName . '/repository')); 45 | } 46 | 47 | public function testDefaultWebRoot() 48 | { 49 | Config::set('docroot', 'www'); 50 | mkdir(Platform::webDir()); 51 | $this->assertTrue(is_dir(self::$tmpName . '/www')); 52 | 53 | Config::set('docroot', 'web'); 54 | mkdir(Platform::webDir()); 55 | $this->assertTrue(is_dir(self::$tmpName . '/web')); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | :`. 16 | relationships: 17 | database: "mysql:mysql" 18 | postgres: "pgmigrationsource:postgresql" 19 | solr: "solrsearch:solr" 20 | redis: "redis:redis" 21 | 22 | # The configuration of app when it is exposed to the web. 23 | web: 24 | # The public directory of the app, relative to its root. 25 | document_root: "/" 26 | # The front-controller script to send non-static requests to. 27 | passthru: "/index.php" 28 | whitelist: 29 | # CSS and Javascript. 30 | - \.css$ 31 | - \.js$ 32 | - \.hbs$ 33 | 34 | # image/* types. 35 | - \.gif$ 36 | - \.jpe?g$ 37 | - \.png$ 38 | - \.tiff?$ 39 | - \.wbmp$ 40 | - \.ico$ 41 | - \.jng$ 42 | - \.bmp$ 43 | - \.svgz?$ 44 | 45 | # audio/* types. 46 | - \.midi?$ 47 | - \.mpe?ga$ 48 | - \.mp2$ 49 | - \.mp3$ 50 | - \.m4a$ 51 | - \.ra$ 52 | - \.weba$ 53 | 54 | # video/* types. 55 | - \.3gpp?$ 56 | - \.mp4$ 57 | - \.mpe?g$ 58 | - \.mpe$ 59 | - \.ogv$ 60 | - \.mov$ 61 | - \.webm$ 62 | - \.flv$ 63 | - \.mng$ 64 | - \.asx$ 65 | - \.asf$ 66 | - \.wmv$ 67 | - \.avi$ 68 | 69 | # application/ogg. 70 | - \.ogx$ 71 | 72 | # application/x-shockwave-flash. 73 | - \.swf$ 74 | 75 | # application/java-archive. 76 | - \.jar$ 77 | 78 | # fonts types. 79 | - \.ttf$ 80 | - \.eot$ 81 | - \.woff$ 82 | - \.otf$ 83 | 84 | # robots.txt. 85 | - /robots\.txt$ 86 | 87 | # html files. 88 | - \.htm?l$ 89 | - \.xml$ 90 | 91 | # application/json. 92 | - \.json$ 93 | 94 | # The size of the persistent disk of the application (in MB). 95 | disk: 3000 96 | 97 | # The mounts that will be performed when the package is deployed. 98 | mounts: 99 | "/public/sites/default/files": "shared:files/files" 100 | "/tmp": "shared:files/tmp" 101 | "/private": "shared:files/private" 102 | 103 | # The build-time dependencies of the app. 104 | dependencies: 105 | php: 106 | "drush/drush": "^8.0" 107 | 108 | runtime: 109 | extensions: 110 | - redis 111 | - pdo_pgsql 112 | # The hooks executed at various points in the lifecycle of the application. 113 | hooks: 114 | # We run deploy hook after your application has been deployed and started. 115 | build: | 116 | mv public/sites/default/google2b5ec40c8ce33883.html public/google2b5ec40c8ce33883.html 117 | cd public 118 | rm README.txt CHANGELOG.txt COPYRIGHT.txt INSTALL.mysql.txt INSTALL.pgsql.txt INSTALL.sqlite.txt INSTALL.txt LICENSE.txt MAINTAINERS.txt PATCHES.txt UPGRADE.txt 119 | deploy: | 120 | composer install --working-dir=public/sites/default/files/composer 121 | cd public 122 | drush -y updatedb 123 | drush revert 124 | drush fr-all -y 125 | drush cc all 126 | mkdir -p /tmp/tools 127 | cp sites/default/tests/composer.json /tmp/tools/ 128 | cd /tmp/tools/ 129 | composer install 130 | 131 | # The configuration of scheduled execution. 132 | crons: 133 | drupal: 134 | spec: "*/15 * * * *" 135 | cmd: "cd public ; drush cron-run" 136 | -------------------------------------------------------------------------------- /tests/fixtures/5.6.platform.app.yaml: -------------------------------------------------------------------------------- 1 | # This file describes an application. You can have multiple applications 2 | # in the same project. 3 | 4 | # The name of this app. Must be unique within a project. 5 | name: php 6 | 7 | # The toolstack used to build the application. 8 | type: php:5.6 9 | build: 10 | flavor: drupal 11 | 12 | # The relationships of the application with services or other applications. 13 | # The left-hand side is the name of the relationship as it will be exposed 14 | # to the application in the PLATFORM_RELATIONSHIPS variable. The right-hand 15 | # side is in the form `:`. 16 | relationships: 17 | database: "mysql:mysql" 18 | 19 | # The configuration of app when it is exposed to the web. 20 | web: 21 | # The public directory of the app, relative to its root. 22 | document_root: "/" 23 | # The front-controller script to send non-static requests to. 24 | passthru: "/index.php" 25 | whitelist: 26 | # CSS and Javascript. 27 | - \.css$ 28 | - \.js$ 29 | - \.hbs$ 30 | 31 | # image/* types. 32 | - \.gif$ 33 | - \.jpe?g$ 34 | - \.png$ 35 | - \.tiff?$ 36 | - \.wbmp$ 37 | - \.ico$ 38 | - \.jng$ 39 | - \.bmp$ 40 | - \.svgz?$ 41 | 42 | # audio/* types. 43 | - \.midi?$ 44 | - \.mpe?ga$ 45 | - \.mp2$ 46 | - \.mp3$ 47 | - \.m4a$ 48 | - \.ra$ 49 | - \.weba$ 50 | 51 | # video/* types. 52 | - \.3gpp?$ 53 | - \.mp4$ 54 | - \.mpe?g$ 55 | - \.mpe$ 56 | - \.ogv$ 57 | - \.mov$ 58 | - \.webm$ 59 | - \.flv$ 60 | - \.mng$ 61 | - \.asx$ 62 | - \.asf$ 63 | - \.wmv$ 64 | - \.avi$ 65 | 66 | # application/ogg. 67 | - \.ogx$ 68 | 69 | # application/x-shockwave-flash. 70 | - \.swf$ 71 | 72 | # application/java-archive. 73 | - \.jar$ 74 | 75 | # fonts types. 76 | - \.ttf$ 77 | - \.eot$ 78 | - \.woff$ 79 | - \.otf$ 80 | 81 | # robots.txt. 82 | - /robots\.txt$ 83 | 84 | # html files. 85 | - \.htm?l$ 86 | - \.xml$ 87 | 88 | # application/json. 89 | - \.json$ 90 | 91 | # The size of the persistent disk of the application (in MB). 92 | disk: 3000 93 | 94 | # The mounts that will be performed when the package is deployed. 95 | mounts: 96 | "/public/sites/default/files": "shared:files/files" 97 | "/tmp": "shared:files/tmp" 98 | "/private": "shared:files/private" 99 | 100 | # The build-time dependencies of the app. 101 | dependencies: 102 | php: 103 | "drush/drush": "^8.0" 104 | 105 | runtime: 106 | extensions: 107 | - redis 108 | - pdo_pgsql 109 | # The hooks executed at various points in the lifecycle of the application. 110 | hooks: 111 | # We run deploy hook after your application has been deployed and started. 112 | build: | 113 | mv public/sites/default/google2b5ec40c8ce33883.html public/google2b5ec40c8ce33883.html 114 | cd public 115 | rm README.txt CHANGELOG.txt COPYRIGHT.txt INSTALL.mysql.txt INSTALL.pgsql.txt INSTALL.sqlite.txt INSTALL.txt LICENSE.txt MAINTAINERS.txt PATCHES.txt UPGRADE.txt 116 | deploy: | 117 | composer install --working-dir=public/sites/default/files/composer 118 | cd public 119 | drush -y updatedb 120 | drush revert 121 | drush fr-all -y 122 | drush cc all 123 | mkdir -p /tmp/tools 124 | cp sites/default/tests/composer.json /tmp/tools/ 125 | cd /tmp/tools/ 126 | composer install 127 | 128 | # The configuration of scheduled execution. 129 | crons: 130 | drupal: 131 | spec: "*/15 * * * *" 132 | cmd: "cd public ; drush cron-run" 133 | -------------------------------------------------------------------------------- /tests/fixtures/services.yaml: -------------------------------------------------------------------------------- 1 | # The services of the project. 2 | # 3 | # Each service listed will be deployed 4 | # to power your Platform.sh project. 5 | 6 | mysqldb: 7 | type: "mysql:10.0" 8 | disk: 2048 9 | # 10 | #rediscache: 11 | # type: "redis:3.0" 12 | # 13 | solrsearch: 14 | type: "solr:6.3" 15 | disk: 1024 16 | -------------------------------------------------------------------------------- /tests/fixtures/solr-3-services.yaml: -------------------------------------------------------------------------------- 1 | # The services of the project. 2 | # 3 | # Each service listed will be deployed 4 | # to power your Platform.sh project. 5 | 6 | mysqldb: 7 | type: "mysql:10.0" 8 | disk: 2048 9 | # 10 | #rediscache: 11 | # type: "redis:3.0" 12 | # 13 | solrsearch: 14 | type: "solr:3.6" 15 | disk: 1024 16 | --------------------------------------------------------------------------------