diff --git a/.travis.yml b/.travis.yml index 6b8a96a5..8713732b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ -language: generic +dist: xenial -services: - - docker +language: minimal env: global: @@ -13,6 +12,7 @@ env: - VERSION=7.0 - VERSION=7.1 - VERSION=7.2 + - VERSION=7.3 # Skip building master. # Stable image tags are pushed during release tag builds so that stable and release tags match on Docker Hub. diff --git a/5.6/.dockerignore b/5.6/.dockerignore deleted file mode 100644 index 95db102e..00000000 --- a/5.6/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -README.md -LICENSE diff --git a/5.6/Dockerfile b/5.6/Dockerfile index 5f2533da..9cb4e882 100644 --- a/5.6/Dockerfile +++ b/5.6/Dockerfile @@ -1,13 +1,3 @@ -# Build mhsendmail -FROM golang:1.8.3 as mhbuild -ENV MHSENDMAIL_VERSION=0.2.0 -RUN set -xe; \ - go get -d github.com/mailhog/mhsendmail; \ - cd /go/src/github.com/mailhog/mhsendmail; \ - git checkout tags/v${MHSENDMAIL_VERSION}; \ - go get github.com/mailhog/mhsendmail - -# Build the image FROM php:5.6-fpm ARG DEBIAN_FRONTEND=noninteractive @@ -41,7 +31,7 @@ ENV LC_ALL en_US.utf8 RUN set -xe; \ sed -i 's/main/main contrib non-free/' /etc/apt/sources.list; \ # blackfire.io repo - curl -fsSL https://packagecloud.io/gpg.key | apt-key add -; \ + curl -fsSL https://packages.blackfire.io/gpg.key | apt-key add -; \ echo "deb https://packages.blackfire.io/debian any main" | tee /etc/apt/sources.list.d/blackfire.list; \ # git-lfs repo curl -fsSL https://packagecloud.io/github/git-lfs/gpgkey | apt-key add -; \ @@ -68,6 +58,7 @@ RUN set -xe; \ # cgi-fcgi binary - used for self-testing (php-fpm) libfcgi-bin \ mc \ + msmtp \ mysql-client \ nano \ openssh-client \ @@ -85,13 +76,15 @@ RUN set -xe; \ apt-get clean; rm -rf /var/lib/apt/lists/* RUN set -xe; \ - # Create a regular user/group "docker" (uid = 1000, gid = 1000 ) with access to sudo + # Create a regular user/group "docker" (uid = 1000, gid = 1000 ) groupadd docker -g 1000; \ - useradd -m -s /bin/bash -u 1000 -g 1000 -G sudo -p docker docker; \ + useradd -m -s /bin/bash -u 1000 -g 1000 -p docker docker; \ + # Give the docker user sudo access + usermod -a -G sudo docker; \ echo 'docker ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers ENV GOSU_VERSION=1.10 \ - GOMPLATE_VERSION=2.4.0 + GOMPLATE_VERSION=3.0.0 RUN set -xe; \ # Install gosu and give access to the docker user primary group to use it. # gosu is used instead of sudo to start the main container process (pid 1) in a docker friendly way. @@ -135,6 +128,8 @@ RUN set -xe; \ libpng-dev \ libpq-dev \ libssh2-1-dev \ + libxpm-dev \ + libvpx-dev \ libxslt1-dev \ zlib1g-dev \ "; \ @@ -156,10 +151,17 @@ RUN set -xe; \ libpq5 \ libssh2-1 \ libsybdb5 \ + libxpm4 \ + libvpx4 \ libxslt1.1 \ zlib1g \ ;\ - docker-php-ext-configure >/dev/null gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/; \ + docker-php-ext-configure >/dev/null gd \ + --with-freetype-dir=/usr/include/ \ + --with-jpeg-dir=/usr/include/ \ + --with-vpx-dir=/usr/include/ \ + --with-png-dir=/usr/include/ \ + --with-xpm-dir=/usr/include/; \ docker-php-ext-configure >/dev/null imap --with-kerberos --with-imap-ssl; \ docker-php-ext-configure >/dev/null ldap --with-libdir=lib/x86_64-linux-gnu/; \ # mssql can be install in PHP 5.6 only. pdo_dblib is available in PHP 7.0+ as well. @@ -211,8 +213,6 @@ RUN set -xe; \ redis \ ssh2 \ ;\ - # Disable xdebug by default - rm -f /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; \ # Cleanup docker-php-source delete; \ rm -rf /tmp/pear ~/.pearrc; \ @@ -220,13 +220,13 @@ RUN set -xe; \ apt-get clean; rm -rf /var/lib/apt/lists/* # PHP tools (installed globally) -ENV COMPOSER_VERSION=1.6.3 \ - DRUSH_VERSION=8.1.16 \ +ENV COMPOSER_VERSION=1.8.0 \ + DRUSH_VERSION=8.1.18 \ DRUSH_LAUNCHER_VERSION=0.6.0 \ - DRUPAL_CONSOLE_LAUNCHER_VERSION=1.7.0 \ - WPCLI_VERSION=1.5.0 \ - BLACKFIRE_VERSION=1.15.0 \ - PLATFORMSH_CLI_VERSION=3.33.5 + DRUPAL_CONSOLE_LAUNCHER_VERSION=1.8.0 \ + WPCLI_VERSION=2.0.1 \ + BLACKFIRE_VERSION=1.22.0 \ + PLATFORMSH_CLI_VERSION=3.38.1 RUN set -xe; \ # Composer curl -fsSL "https://github.com/composer/composer/releases/download/${COMPOSER_VERSION}/composer.phar" -o /usr/local/bin/composer; \ @@ -245,42 +245,31 @@ RUN set -xe; \ # Make all downloaded binaries executable in one shot (cd /usr/local/bin && chmod +x composer drush8 drush drupal wp blackfire platform); -# Ruby -RUN set -xe; \ - apt-get update >/dev/null; \ - apt-get -y --no-install-recommends install >/dev/null \ - ruby-full \ - rlwrap \ - ;\ - # Cleanup - apt-get clean; rm -rf /var/lib/apt/lists/*; \ - # bundler - gem install bundler >/dev/null; \ - # Have bundler install gems locally (./.bundle) by default - echo "\n""export BUNDLE_PATH=.bundle" >> /home/docker/.profile - # All further RUN commands will run as the "docker" user USER docker +SHELL ["/bin/bash", "-c"] # PHP tools (installed as user) -ENV MG_CODEGEN_VERSION=1.10 \ - TERMINUS_VERSION=1.8.1 -RUN set -xe; \ +ENV MG_CODEGEN_VERSION=1.10.2 \ + TERMINUS_VERSION=1.9.0 \ + DRUSH_BACKDROP_VERSION=0.1.0 +# Don't use -x here, as the output may be excessive +RUN set -e; \ \ # Set drush8 as a global fallback for Drush Launcher - echo "\n""export DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8" >> $HOME/.profile; \ + echo -e "\n""export DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8" >> $HOME/.profile; \ # Composer based dependencies # Add composer bin directory to PATH - echo "\n"'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> $HOME/.profile; \ + echo -e "\n"'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> $HOME/.profile; \ # Reload updated PATH from profile to make composer/drush/etc. visible below . $HOME/.profile; \ # Install cgr to use it in-place of `composer global require` composer global require consolidation/cgr >/dev/null; \ # Composer parallel install plugin composer global require hirak/prestissimo >/dev/null; \ - # Drupal Coder w/ a matching version of PHP_CodeSniffer - cgr drupal/coder >/dev/null; \ - phpcs --config-set installed_paths $HOME/.composer/global/drupal/coder/vendor/drupal/coder/coder_sniffer; \ + # Drupal Coder & WP Coding Standards w/ a matching version of PHP_CodeSniffer + cgr drupal/coder wp-coding-standards/wpcs >/dev/null; \ + phpcs --config-set installed_paths "$HOME/.composer/global/drupal/coder/vendor/drupal/coder/coder_sniffer/,$HOME/.composer/global/wp-coding-standards/wpcs/vendor/wp-coding-standards/wpcs/"; \ # Magento2 Code Generator cgr staempfli/magento2-code-generator:${MG_CODEGEN_VERSION} >/dev/null; \ # Terminus @@ -290,14 +279,15 @@ RUN set -xe; \ \ # Drush modules drush dl registry_rebuild --default-major=7 --destination=$HOME/.drush >/dev/null; \ + mkdir $HOME/.drush/backdrop && curl -fsSL "https://github.com/backdrop-contrib/drush/archive/${DRUSH_BACKDROP_VERSION}.tar.gz" | tar xz -C $HOME/.drush/backdrop --strip-components 1; \ drush cc drush # Node.js (installed as user) ENV \ - NVM_VERSION=0.33.11 \ - NODE_VERSION=8.11.3 \ - YARN_VERSION=1.8.0 -# Don't use -x here, as nvm prints too much stuff + NVM_VERSION=0.34.0 \ + NODE_VERSION=10.15.0 \ + YARN_VERSION=1.13.0 +# Don't use -x here, as the output may be excessive RUN set -e; \ # NVM and a defaut Node.js version export PROFILE="$HOME/.profile"; \ @@ -308,12 +298,72 @@ RUN set -e; \ export YARN_PROFILE="$HOME/.profile"; \ curl -fsSL https://yarnpkg.com/install.sh | bash -s -- --version ${YARN_VERSION} >/dev/null +# Ruby (installed as user) +ENV \ + RVM_VERSION_INSTALL=1.29.7 \ + RUBY_VERSION_INSTALL=2.6.0 +# Don't use -x here, as the output may be excessive +RUN set -e; \ + # Public GPG servers are not realiable, so downloading keys from rvm.io instead. + #gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB + # Import and trust rvm keys + # mpapis@gmail.com + curl -sSL https://rvm.io/mpapis.asc | gpg --batch --import -; \ + echo 409B6B1796C275462A1703113804BB82D39DC0E3:6: | gpg --batch --import-ownertrust; \ + # piotr.kuczynski@gmail.com + curl -sSL https://rvm.io/pkuczynski.asc | gpg --batch --import -; \ + echo 7D2BAF1CF37B13E2069D6956105BD0E739499BDB:6: | gpg --batch --import-ownertrust; \ + \ + echo 'rvm_autoupdate_flag=0' >> $HOME/.rvmrc; \ + echo 'rvm_silence_path_mismatch_check_flag=1' >> $HOME/.rvmrc; \ + curl -fsSL https://raw.githubusercontent.com/rvm/rvm/${RVM_VERSION_INSTALL}/binscripts/rvm-installer | bash -s -- --ignore-dotfiles --version ${RVM_VERSION_INSTALL}; \ + { \ + echo ''; \ + echo 'export PATH="$PATH:$HOME/.rvm/bin"'; \ + echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"'; \ + } >> $HOME/.profile; \ + # Reload $HOME/.profile to apply settings for the current shell + . $HOME/.profile; \ + \ + # rvm.io does not currently have ruby binaries for Debian 9, so Ruby is compiled from source, which requires a bunch + # of extra dependencies installed (rvm installs these automatically), which bloat this image: + # rvm/ruby required packages: gawk, automake, bison, libffi-dev, libgdbm-dev, libncurses5-dev, libsqlite3-dev, libtool, libyaml-dev, sqlite3, zlib1g-dev, libgmp-dev, libreadline-dev, libssl-dev + rvm install ruby-${RUBY_VERSION_INSTALL}; \ + rvm use ruby-${RUBY_VERSION_INSTALL} --default; \ + \ + gem install bundler; \ + # Have bundler install gems locally (./.bundle) by default + echo -e "\n"'export BUNDLE_PATH=.bundle' >> $HOME/.profile; \ + \ + rvm cleanup all; \ + rvm gemset globalcache enable + +# Python (installed as user) +ENV \ + PYENV_VERSION_INSTALL=1.2.9 +# PYTHON_VERSION_INSTALL=3.7.0 +RUN set -xe; \ + git clone --depth 1 -b v${PYENV_VERSION_INSTALL} https://github.com/pyenv/pyenv.git $HOME/.pyenv; \ + rm -rf $HOME/.pyenv/.git; \ + { \ + echo ''; \ + echo 'export PYENV_ROOT="$HOME/.pyenv"'; \ + echo 'export PATH="$PYENV_ROOT/bin:$PATH"'; \ + echo 'eval "$(pyenv init -)"'; \ + } >> $HOME/.profile +# Disabled for now - uses too much space (~200MB). +# The upsteam Debian 9 image ships with Python 2.7 out of the box, so we'll stick with that by default. +# # Reload $HOME/.profile to apply settings for the current shell +# . $HOME/.profile; \ +# pyenv install ${PYTHON_VERSION_INSTALL}; \ +# pyenv global ${PYTHON_VERSION_INSTALL} + USER root +SHELL ["/bin/sh", "-c"] -# Copy mhsendmail binary from stage 1 -COPY --from=mhbuild /go/bin/mhsendmail /usr/local/bin/mhsendmail # Copy configs and scripts COPY --chown=docker:docker config/.drush /home/docker/.drush +COPY --chown=docker:docker config/.terminus /home/docker/.terminus COPY --chown=docker:docker config/.ssh /home/docker/.ssh COPY config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf COPY startup.sh /opt/startup.sh diff --git a/5.6/config/.terminus/config.yml b/5.6/config/.terminus/config.yml new file mode 100644 index 00000000..c7e89515 --- /dev/null +++ b/5.6/config/.terminus/config.yml @@ -0,0 +1,2 @@ +hide_update_message: true +hide_git_mode_warning: true \ No newline at end of file diff --git a/5.6/config/php/zz-php.ini b/5.6/config/php/zz-php.ini index cc51d5c0..08317d70 100644 --- a/5.6/config/php/zz-php.ini +++ b/5.6/config/php/zz-php.ini @@ -8,8 +8,10 @@ sendmail_path = /bin/true date.timezone = UTC display_errors = On display_startup_errors = On + +[mail] ; Enable Mailhog integration by default -sendmail_path = '/usr/local/bin/mhsendmail --smtp-addr=mail:1025' +sendmail_path = '/usr/bin/msmtp -t --host=mail --port=1025' ; Extention settings [opcache] diff --git a/5.6/startup.sh b/5.6/startup.sh index f3308350..e445e512 100755 --- a/5.6/startup.sh +++ b/5.6/startup.sh @@ -154,7 +154,7 @@ touch /var/run/cli if [[ -x ${PROJECT_ROOT}/.docksal/services/cli/startup.sh ]]; then echo-debug "Running custom startup script..." # TODO: should we source the script instead? - ${PROJECT_ROOT}/.docksal/services/cli/startup.sh + su -l docker -c "${PROJECT_ROOT}/.docksal/services/cli/startup.sh" if [[ $? == 0 ]]; then echo-debug "Custom startup script executed successfully." else diff --git a/7.0/Dockerfile b/7.0/Dockerfile index fb4e5712..c8c040bf 100644 --- a/7.0/Dockerfile +++ b/7.0/Dockerfile @@ -1,13 +1,3 @@ -# Build mhsendmail -FROM golang:1.8.3 as mhbuild -ENV MHSENDMAIL_VERSION=0.2.0 -RUN set -xe; \ - go get -d github.com/mailhog/mhsendmail; \ - cd /go/src/github.com/mailhog/mhsendmail; \ - git checkout tags/v${MHSENDMAIL_VERSION}; \ - go get github.com/mailhog/mhsendmail - -# Build the image FROM php:7.0-fpm ARG DEBIAN_FRONTEND=noninteractive @@ -41,7 +31,7 @@ ENV LC_ALL en_US.utf8 RUN set -xe; \ sed -i 's/main/main contrib non-free/' /etc/apt/sources.list; \ # blackfire.io repo - curl -fsSL https://packagecloud.io/gpg.key | apt-key add -; \ + curl -fsSL https://packages.blackfire.io/gpg.key | apt-key add -; \ echo "deb https://packages.blackfire.io/debian any main" | tee /etc/apt/sources.list.d/blackfire.list; \ # git-lfs repo curl -fsSL https://packagecloud.io/github/git-lfs/gpgkey | apt-key add -; \ @@ -71,6 +61,7 @@ RUN set -xe; \ # cgi-fcgi binary - used for self-testing (php-fpm) libfcgi-bin \ mc \ + msmtp \ mysql-client \ nano \ openssh-client \ @@ -82,19 +73,22 @@ RUN set -xe; \ sudo \ supervisor \ unzip \ + webp \ zip \ ;\ # Cleanup apt-get clean; rm -rf /var/lib/apt/lists/* RUN set -xe; \ - # Create a regular user/group "docker" (uid = 1000, gid = 1000 ) with access to sudo + # Create a regular user/group "docker" (uid = 1000, gid = 1000 ) groupadd docker -g 1000; \ - useradd -m -s /bin/bash -u 1000 -g 1000 -G sudo -p docker docker; \ + useradd -m -s /bin/bash -u 1000 -g 1000 -p docker docker; \ + # Give the docker user sudo access + usermod -a -G sudo docker; \ echo 'docker ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers ENV GOSU_VERSION=1.10 \ - GOMPLATE_VERSION=2.4.0 + GOMPLATE_VERSION=3.0.0 RUN set -xe; \ # Install gosu and give access to the docker user primary group to use it. # gosu is used instead of sudo to start the main container process (pid 1) in a docker friendly way. @@ -136,10 +130,12 @@ RUN set -xe; \ libmhash-dev \ libpng-dev \ libpq-dev \ + libwebp-dev \ libssh2-1-dev \ + libxpm-dev \ libxslt1-dev \ + libzip-dev \ unixodbc-dev \ - zlib1g-dev \ "; \ apt-get update >/dev/null; \ # Necessary for msodbcsql17 (MSSQL) @@ -162,17 +158,24 @@ RUN set -xe; \ libpng16-16 \ libpq5 \ libssh2-1 \ + libxpm4 \ libxslt1.1 \ + libzip4 \ msodbcsql17 \ - zlib1g \ ;\ - # SSH2 must be installed from source + # SSH2 must be installed from source for PHP 7.x git clone https://github.com/php/pecl-networking-ssh2.git /usr/src/php/ext/ssh2 && rm -rf /usr/src/php/ext/ssh2/.git; \ \ - docker-php-ext-configure >/dev/null gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/; \ + docker-php-ext-configure >/dev/null gd \ + --with-freetype-dir=/usr/include/ \ + --with-jpeg-dir=/usr/include/ \ + --with-webp-dir=/usr/include/ \ + --with-png-dir=/usr/include/ \ + --with-xpm-dir=/usr/include/; \ docker-php-ext-configure >/dev/null imap --with-kerberos --with-imap-ssl; \ docker-php-ext-configure >/dev/null ldap --with-libdir=lib/x86_64-linux-gnu/; \ docker-php-ext-configure >/dev/null pgsql --with-pgsql=/usr/local/pgsql/; \ + docker-php-ext-configure >/dev/null zip --with-libzip; \ \ docker-php-ext-install >/dev/null -j$(nproc) \ bcmath \ @@ -218,8 +221,6 @@ RUN set -xe; \ redis \ sqlsrv \ ;\ - # Disable xdebug by default - rm -f /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; \ # Cleanup docker-php-source delete; \ rm -rf /tmp/pear ~/.pearrc; \ @@ -227,13 +228,13 @@ RUN set -xe; \ apt-get clean; rm -rf /var/lib/apt/lists/* # PHP tools (installed globally) -ENV COMPOSER_VERSION=1.6.3 \ - DRUSH_VERSION=8.1.16 \ +ENV COMPOSER_VERSION=1.8.0 \ + DRUSH_VERSION=8.1.18 \ DRUSH_LAUNCHER_VERSION=0.6.0 \ - DRUPAL_CONSOLE_LAUNCHER_VERSION=1.7.0 \ - WPCLI_VERSION=1.5.0 \ - BLACKFIRE_VERSION=1.15.0 \ - PLATFORMSH_CLI_VERSION=3.33.5 + DRUPAL_CONSOLE_LAUNCHER_VERSION=1.8.0 \ + WPCLI_VERSION=2.0.1 \ + BLACKFIRE_VERSION=1.22.0 \ + PLATFORMSH_CLI_VERSION=3.38.1 RUN set -xe; \ # Composer curl -fsSL "https://github.com/composer/composer/releases/download/${COMPOSER_VERSION}/composer.phar" -o /usr/local/bin/composer; \ @@ -252,42 +253,31 @@ RUN set -xe; \ # Make all downloaded binaries executable in one shot (cd /usr/local/bin && chmod +x composer drush8 drush drupal wp blackfire platform); -# Ruby -RUN set -xe; \ - apt-get update >/dev/null; \ - apt-get -y --no-install-recommends install >/dev/null \ - ruby-full \ - rlwrap \ - ;\ - # Cleanup - apt-get clean; rm -rf /var/lib/apt/lists/*; \ - # bundler - gem install bundler >/dev/null; \ - # Have bundler install gems locally (./.bundle) by default - echo "\n""export BUNDLE_PATH=.bundle" >> /home/docker/.profile - # All further RUN commands will run as the "docker" user USER docker +SHELL ["/bin/bash", "-c"] # PHP tools (installed as user) -ENV MG_CODEGEN_VERSION=1.10 \ - TERMINUS_VERSION=1.8.1 -RUN set -xe; \ +ENV MG_CODEGEN_VERSION=1.10.2 \ + TERMINUS_VERSION=1.9.0 \ + DRUSH_BACKDROP_VERSION=0.1.0 +# Don't use -x here, as the output may be excessive +RUN set -e; \ \ # Set drush8 as a global fallback for Drush Launcher - echo "\n""export DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8" >> $HOME/.profile; \ + echo -e "\n""export DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8" >> $HOME/.profile; \ # Composer based dependencies # Add composer bin directory to PATH - echo "\n"'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> $HOME/.profile; \ + echo -e "\n"'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> $HOME/.profile; \ # Reload updated PATH from profile to make composer/drush/etc. visible below . $HOME/.profile; \ # Install cgr to use it in-place of `composer global require` composer global require consolidation/cgr >/dev/null; \ # Composer parallel install plugin composer global require hirak/prestissimo >/dev/null; \ - # Drupal Coder w/ a matching version of PHP_CodeSniffer - cgr drupal/coder >/dev/null; \ - phpcs --config-set installed_paths $HOME/.composer/global/drupal/coder/vendor/drupal/coder/coder_sniffer; \ + # Drupal Coder & WP Coding Standards w/ a matching version of PHP_CodeSniffer + cgr drupal/coder wp-coding-standards/wpcs >/dev/null; \ + phpcs --config-set installed_paths "$HOME/.composer/global/drupal/coder/vendor/drupal/coder/coder_sniffer/,$HOME/.composer/global/wp-coding-standards/wpcs/vendor/wp-coding-standards/wpcs/"; \ # Magento2 Code Generator cgr staempfli/magento2-code-generator:${MG_CODEGEN_VERSION} >/dev/null; \ # Terminus @@ -297,14 +287,15 @@ RUN set -xe; \ \ # Drush modules drush dl registry_rebuild --default-major=7 --destination=$HOME/.drush >/dev/null; \ + mkdir $HOME/.drush/backdrop && curl -fsSL "https://github.com/backdrop-contrib/drush/archive/${DRUSH_BACKDROP_VERSION}.tar.gz" | tar xz -C $HOME/.drush/backdrop --strip-components 1; \ drush cc drush # Node.js (installed as user) ENV \ - NVM_VERSION=0.33.11 \ - NODE_VERSION=8.11.3 \ - YARN_VERSION=1.8.0 -# Don't use -x here, as nvm prints too much stuff + NVM_VERSION=0.34.0 \ + NODE_VERSION=10.15.0 \ + YARN_VERSION=1.13.0 +# Don't use -x here, as the output may be excessive RUN set -e; \ # NVM and a defaut Node.js version export PROFILE="$HOME/.profile"; \ @@ -315,12 +306,72 @@ RUN set -e; \ export YARN_PROFILE="$HOME/.profile"; \ curl -fsSL https://yarnpkg.com/install.sh | bash -s -- --version ${YARN_VERSION} >/dev/null +# Ruby (installed as user) +ENV \ + RVM_VERSION_INSTALL=1.29.7 \ + RUBY_VERSION_INSTALL=2.6.0 +# Don't use -x here, as the output may be excessive +RUN set -e; \ + # Public GPG servers are not realiable, so downloading keys from rvm.io instead. + #gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB + # Import and trust rvm keys + # mpapis@gmail.com + curl -sSL https://rvm.io/mpapis.asc | gpg --batch --import -; \ + echo 409B6B1796C275462A1703113804BB82D39DC0E3:6: | gpg --batch --import-ownertrust; \ + # piotr.kuczynski@gmail.com + curl -sSL https://rvm.io/pkuczynski.asc | gpg --batch --import -; \ + echo 7D2BAF1CF37B13E2069D6956105BD0E739499BDB:6: | gpg --batch --import-ownertrust; \ + \ + echo 'rvm_autoupdate_flag=0' >> $HOME/.rvmrc; \ + echo 'rvm_silence_path_mismatch_check_flag=1' >> $HOME/.rvmrc; \ + curl -fsSL https://raw.githubusercontent.com/rvm/rvm/${RVM_VERSION_INSTALL}/binscripts/rvm-installer | bash -s -- --ignore-dotfiles --version ${RVM_VERSION_INSTALL}; \ + { \ + echo ''; \ + echo 'export PATH="$PATH:$HOME/.rvm/bin"'; \ + echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"'; \ + } >> $HOME/.profile; \ + # Reload $HOME/.profile to apply settings for the current shell + . $HOME/.profile; \ + \ + # rvm.io does not currently have ruby binaries for Debian 9, so Ruby is compiled from source, which requires a bunch + # of extra dependencies installed (rvm installs these automatically), which bloat this image: + # rvm/ruby required packages: gawk, automake, bison, libffi-dev, libgdbm-dev, libncurses5-dev, libsqlite3-dev, libtool, libyaml-dev, sqlite3, zlib1g-dev, libgmp-dev, libreadline-dev, libssl-dev + rvm install ruby-${RUBY_VERSION_INSTALL}; \ + rvm use ruby-${RUBY_VERSION_INSTALL} --default; \ + \ + gem install bundler; \ + # Have bundler install gems locally (./.bundle) by default + echo -e "\n"'export BUNDLE_PATH=.bundle' >> $HOME/.profile; \ + \ + rvm cleanup all; \ + rvm gemset globalcache enable + +# Python (installed as user) +ENV \ + PYENV_VERSION_INSTALL=1.2.9 +# PYTHON_VERSION_INSTALL=3.7.0 +RUN set -xe; \ + git clone --depth 1 -b v${PYENV_VERSION_INSTALL} https://github.com/pyenv/pyenv.git $HOME/.pyenv; \ + rm -rf $HOME/.pyenv/.git; \ + { \ + echo ''; \ + echo 'export PYENV_ROOT="$HOME/.pyenv"'; \ + echo 'export PATH="$PYENV_ROOT/bin:$PATH"'; \ + echo 'eval "$(pyenv init -)"'; \ + } >> $HOME/.profile +# Disabled for now - uses too much space (~200MB). +# The upsteam Debian 9 image ships with Python 2.7 out of the box, so we'll stick with that by default. +# # Reload $HOME/.profile to apply settings for the current shell +# . $HOME/.profile; \ +# pyenv install ${PYTHON_VERSION_INSTALL}; \ +# pyenv global ${PYTHON_VERSION_INSTALL} + USER root +SHELL ["/bin/sh", "-c"] -# Copy mhsendmail binary from stage 1 -COPY --from=mhbuild /go/bin/mhsendmail /usr/local/bin/mhsendmail # Copy configs and scripts COPY --chown=docker:docker config/.drush /home/docker/.drush +COPY --chown=docker:docker config/.terminus /home/docker/.terminus COPY --chown=docker:docker config/.ssh /home/docker/.ssh COPY config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf COPY startup.sh /opt/startup.sh diff --git a/7.0/config/.terminus/config.yml b/7.0/config/.terminus/config.yml new file mode 100644 index 00000000..c7e89515 --- /dev/null +++ b/7.0/config/.terminus/config.yml @@ -0,0 +1,2 @@ +hide_update_message: true +hide_git_mode_warning: true \ No newline at end of file diff --git a/7.0/config/php/zz-php.ini b/7.0/config/php/zz-php.ini index 0b1aff21..dd78b97d 100644 --- a/7.0/config/php/zz-php.ini +++ b/7.0/config/php/zz-php.ini @@ -7,8 +7,10 @@ sendmail_path = /bin/true date.timezone = UTC display_errors = On display_startup_errors = On + +[mail] ; Enable Mailhog integration by default -sendmail_path = '/usr/local/bin/mhsendmail --smtp-addr=mail:1025' +sendmail_path = '/usr/bin/msmtp -t --host=mail --port=1025' ; Extention settings [opcache] diff --git a/7.0/startup.sh b/7.0/startup.sh index f3308350..e445e512 100755 --- a/7.0/startup.sh +++ b/7.0/startup.sh @@ -154,7 +154,7 @@ touch /var/run/cli if [[ -x ${PROJECT_ROOT}/.docksal/services/cli/startup.sh ]]; then echo-debug "Running custom startup script..." # TODO: should we source the script instead? - ${PROJECT_ROOT}/.docksal/services/cli/startup.sh + su -l docker -c "${PROJECT_ROOT}/.docksal/services/cli/startup.sh" if [[ $? == 0 ]]; then echo-debug "Custom startup script executed successfully." else diff --git a/7.1/Dockerfile b/7.1/Dockerfile index 439faf7f..48b7dd24 100644 --- a/7.1/Dockerfile +++ b/7.1/Dockerfile @@ -1,13 +1,3 @@ -# Build mhsendmail -FROM golang:1.8.3 as mhbuild -ENV MHSENDMAIL_VERSION=0.2.0 -RUN set -xe; \ - go get -d github.com/mailhog/mhsendmail; \ - cd /go/src/github.com/mailhog/mhsendmail; \ - git checkout tags/v${MHSENDMAIL_VERSION}; \ - go get github.com/mailhog/mhsendmail - -# Build the image FROM php:7.1-fpm ARG DEBIAN_FRONTEND=noninteractive @@ -41,7 +31,7 @@ ENV LC_ALL en_US.utf8 RUN set -xe; \ sed -i 's/main/main contrib non-free/' /etc/apt/sources.list; \ # blackfire.io repo - curl -fsSL https://packagecloud.io/gpg.key | apt-key add -; \ + curl -fsSL https://packages.blackfire.io/gpg.key | apt-key add -; \ echo "deb https://packages.blackfire.io/debian any main" | tee /etc/apt/sources.list.d/blackfire.list; \ # git-lfs repo curl -fsSL https://packagecloud.io/github/git-lfs/gpgkey | apt-key add -; \ @@ -71,6 +61,7 @@ RUN set -xe; \ # cgi-fcgi binary - used for self-testing (php-fpm) libfcgi-bin \ mc \ + msmtp \ mysql-client \ nano \ openssh-client \ @@ -82,19 +73,22 @@ RUN set -xe; \ sudo \ supervisor \ unzip \ + webp \ zip \ ;\ # Cleanup apt-get clean; rm -rf /var/lib/apt/lists/* RUN set -xe; \ - # Create a regular user/group "docker" (uid = 1000, gid = 1000 ) with access to sudo + # Create a regular user/group "docker" (uid = 1000, gid = 1000 ) groupadd docker -g 1000; \ - useradd -m -s /bin/bash -u 1000 -g 1000 -G sudo -p docker docker; \ + useradd -m -s /bin/bash -u 1000 -g 1000 -p docker docker; \ + # Give the docker user sudo access + usermod -a -G sudo docker; \ echo 'docker ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers ENV GOSU_VERSION=1.10 \ - GOMPLATE_VERSION=2.4.0 + GOMPLATE_VERSION=3.0.0 RUN set -xe; \ # Install gosu and give access to the docker user primary group to use it. # gosu is used instead of sudo to start the main container process (pid 1) in a docker friendly way. @@ -136,10 +130,12 @@ RUN set -xe; \ libmhash-dev \ libpng-dev \ libpq-dev \ + libwebp-dev \ libssh2-1-dev \ + libxpm-dev \ libxslt1-dev \ + libzip-dev \ unixodbc-dev \ - zlib1g-dev \ "; \ apt-get update >/dev/null; \ # Necessary for msodbcsql17 (MSSQL) @@ -162,17 +158,24 @@ RUN set -xe; \ libpng16-16 \ libpq5 \ libssh2-1 \ + libxpm4 \ libxslt1.1 \ + libzip4 \ msodbcsql17 \ - zlib1g \ ;\ - # SSH2 must be installed from source + # SSH2 must be installed from source for PHP 7.x git clone https://github.com/php/pecl-networking-ssh2.git /usr/src/php/ext/ssh2 && rm -rf /usr/src/php/ext/ssh2/.git; \ \ - docker-php-ext-configure >/dev/null gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/; \ + docker-php-ext-configure >/dev/null gd \ + --with-freetype-dir=/usr/include/ \ + --with-jpeg-dir=/usr/include/ \ + --with-webp-dir=/usr/include/ \ + --with-png-dir=/usr/include/ \ + --with-xpm-dir=/usr/include/; \ docker-php-ext-configure >/dev/null imap --with-kerberos --with-imap-ssl; \ docker-php-ext-configure >/dev/null ldap --with-libdir=lib/x86_64-linux-gnu/; \ docker-php-ext-configure >/dev/null pgsql --with-pgsql=/usr/local/pgsql/; \ + docker-php-ext-configure >/dev/null zip --with-libzip; \ \ docker-php-ext-install >/dev/null -j$(nproc) \ bcmath \ @@ -184,6 +187,7 @@ RUN set -xe; \ imap \ intl \ ldap \ + # mcrypt is deprecated in 7.1 and removed in 7.2. See Deprecated features. mcrypt \ mysqli \ opcache \ @@ -218,8 +222,6 @@ RUN set -xe; \ redis \ sqlsrv \ ;\ - # Disable xdebug by default - rm -f /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; \ # Cleanup docker-php-source delete; \ rm -rf /tmp/pear ~/.pearrc; \ @@ -227,13 +229,13 @@ RUN set -xe; \ apt-get clean; rm -rf /var/lib/apt/lists/* # PHP tools (installed globally) -ENV COMPOSER_VERSION=1.6.3 \ - DRUSH_VERSION=8.1.16 \ +ENV COMPOSER_VERSION=1.8.0 \ + DRUSH_VERSION=8.1.18 \ DRUSH_LAUNCHER_VERSION=0.6.0 \ - DRUPAL_CONSOLE_LAUNCHER_VERSION=1.7.0 \ - WPCLI_VERSION=1.5.0 \ - BLACKFIRE_VERSION=1.15.0 \ - PLATFORMSH_CLI_VERSION=3.33.5 + DRUPAL_CONSOLE_LAUNCHER_VERSION=1.8.0 \ + WPCLI_VERSION=2.0.1 \ + BLACKFIRE_VERSION=1.22.0 \ + PLATFORMSH_CLI_VERSION=3.38.1 RUN set -xe; \ # Composer curl -fsSL "https://github.com/composer/composer/releases/download/${COMPOSER_VERSION}/composer.phar" -o /usr/local/bin/composer; \ @@ -252,42 +254,31 @@ RUN set -xe; \ # Make all downloaded binaries executable in one shot (cd /usr/local/bin && chmod +x composer drush8 drush drupal wp blackfire platform); -# Ruby -RUN set -xe; \ - apt-get update >/dev/null; \ - apt-get -y --no-install-recommends install >/dev/null \ - ruby-full \ - rlwrap \ - ;\ - # Cleanup - apt-get clean; rm -rf /var/lib/apt/lists/*; \ - # bundler - gem install bundler >/dev/null; \ - # Have bundler install gems locally (./.bundle) by default - echo "\n""export BUNDLE_PATH=.bundle" >> /home/docker/.profile - # All further RUN commands will run as the "docker" user USER docker +SHELL ["/bin/bash", "-c"] # PHP tools (installed as user) -ENV MG_CODEGEN_VERSION=1.10 \ - TERMINUS_VERSION=1.8.1 -RUN set -xe; \ +ENV MG_CODEGEN_VERSION=1.10.2 \ + TERMINUS_VERSION=1.9.0 \ + DRUSH_BACKDROP_VERSION=0.1.0 +# Don't use -x here, as the output may be excessive +RUN set -e; \ \ # Set drush8 as a global fallback for Drush Launcher - echo "\n""export DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8" >> $HOME/.profile; \ + echo -e "\n""export DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8" >> $HOME/.profile; \ # Composer based dependencies # Add composer bin directory to PATH - echo "\n"'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> $HOME/.profile; \ + echo -e "\n"'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> $HOME/.profile; \ # Reload updated PATH from profile to make composer/drush/etc. visible below . $HOME/.profile; \ # Install cgr to use it in-place of `composer global require` composer global require consolidation/cgr >/dev/null; \ # Composer parallel install plugin composer global require hirak/prestissimo >/dev/null; \ - # Drupal Coder w/ a matching version of PHP_CodeSniffer - cgr drupal/coder >/dev/null; \ - phpcs --config-set installed_paths $HOME/.composer/global/drupal/coder/vendor/drupal/coder/coder_sniffer; \ + # Drupal Coder & WP Coding Standards w/ a matching version of PHP_CodeSniffer + cgr drupal/coder wp-coding-standards/wpcs >/dev/null; \ + phpcs --config-set installed_paths "$HOME/.composer/global/drupal/coder/vendor/drupal/coder/coder_sniffer/,$HOME/.composer/global/wp-coding-standards/wpcs/vendor/wp-coding-standards/wpcs/"; \ # Magento2 Code Generator cgr staempfli/magento2-code-generator:${MG_CODEGEN_VERSION} >/dev/null; \ # Terminus @@ -297,14 +288,15 @@ RUN set -xe; \ \ # Drush modules drush dl registry_rebuild --default-major=7 --destination=$HOME/.drush >/dev/null; \ + mkdir $HOME/.drush/backdrop && curl -fsSL "https://github.com/backdrop-contrib/drush/archive/${DRUSH_BACKDROP_VERSION}.tar.gz" | tar xz -C $HOME/.drush/backdrop --strip-components 1; \ drush cc drush # Node.js (installed as user) ENV \ - NVM_VERSION=0.33.11 \ - NODE_VERSION=8.11.3 \ - YARN_VERSION=1.8.0 -# Don't use -x here, as nvm prints too much stuff + NVM_VERSION=0.34.0 \ + NODE_VERSION=10.15.0 \ + YARN_VERSION=1.13.0 +# Don't use -x here, as the output may be excessive RUN set -e; \ # NVM and a defaut Node.js version export PROFILE="$HOME/.profile"; \ @@ -315,12 +307,72 @@ RUN set -e; \ export YARN_PROFILE="$HOME/.profile"; \ curl -fsSL https://yarnpkg.com/install.sh | bash -s -- --version ${YARN_VERSION} >/dev/null +# Ruby (installed as user) +ENV \ + RVM_VERSION_INSTALL=1.29.7 \ + RUBY_VERSION_INSTALL=2.6.0 +# Don't use -x here, as the output may be excessive +RUN set -e; \ + # Public GPG servers are not realiable, so downloading keys from rvm.io instead. + #gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB + # Import and trust rvm keys + # mpapis@gmail.com + curl -sSL https://rvm.io/mpapis.asc | gpg --batch --import -; \ + echo 409B6B1796C275462A1703113804BB82D39DC0E3:6: | gpg --batch --import-ownertrust; \ + # piotr.kuczynski@gmail.com + curl -sSL https://rvm.io/pkuczynski.asc | gpg --batch --import -; \ + echo 7D2BAF1CF37B13E2069D6956105BD0E739499BDB:6: | gpg --batch --import-ownertrust; \ + \ + echo 'rvm_autoupdate_flag=0' >> $HOME/.rvmrc; \ + echo 'rvm_silence_path_mismatch_check_flag=1' >> $HOME/.rvmrc; \ + curl -fsSL https://raw.githubusercontent.com/rvm/rvm/${RVM_VERSION_INSTALL}/binscripts/rvm-installer | bash -s -- --ignore-dotfiles --version ${RVM_VERSION_INSTALL}; \ + { \ + echo ''; \ + echo 'export PATH="$PATH:$HOME/.rvm/bin"'; \ + echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"'; \ + } >> $HOME/.profile; \ + # Reload $HOME/.profile to apply settings for the current shell + . $HOME/.profile; \ + \ + # rvm.io does not currently have ruby binaries for Debian 9, so Ruby is compiled from source, which requires a bunch + # of extra dependencies installed (rvm installs these automatically), which bloat this image: + # rvm/ruby required packages: gawk, automake, bison, libffi-dev, libgdbm-dev, libncurses5-dev, libsqlite3-dev, libtool, libyaml-dev, sqlite3, zlib1g-dev, libgmp-dev, libreadline-dev, libssl-dev + rvm install ruby-${RUBY_VERSION_INSTALL}; \ + rvm use ruby-${RUBY_VERSION_INSTALL} --default; \ + \ + gem install bundler; \ + # Have bundler install gems locally (./.bundle) by default + echo -e "\n"'export BUNDLE_PATH=.bundle' >> $HOME/.profile; \ + \ + rvm cleanup all; \ + rvm gemset globalcache enable + +# Python (installed as user) +ENV \ + PYENV_VERSION_INSTALL=1.2.9 +# PYTHON_VERSION_INSTALL=3.7.0 +RUN set -xe; \ + git clone --depth 1 -b v${PYENV_VERSION_INSTALL} https://github.com/pyenv/pyenv.git $HOME/.pyenv; \ + rm -rf $HOME/.pyenv/.git; \ + { \ + echo ''; \ + echo 'export PYENV_ROOT="$HOME/.pyenv"'; \ + echo 'export PATH="$PYENV_ROOT/bin:$PATH"'; \ + echo 'eval "$(pyenv init -)"'; \ + } >> $HOME/.profile +# Disabled for now - uses too much space (~200MB). +# The upsteam Debian 9 image ships with Python 2.7 out of the box, so we'll stick with that by default. +# # Reload $HOME/.profile to apply settings for the current shell +# . $HOME/.profile; \ +# pyenv install ${PYTHON_VERSION_INSTALL}; \ +# pyenv global ${PYTHON_VERSION_INSTALL} + USER root +SHELL ["/bin/sh", "-c"] -# Copy mhsendmail binary from stage 1 -COPY --from=mhbuild /go/bin/mhsendmail /usr/local/bin/mhsendmail # Copy configs and scripts COPY --chown=docker:docker config/.drush /home/docker/.drush +COPY --chown=docker:docker config/.terminus /home/docker/.terminus COPY --chown=docker:docker config/.ssh /home/docker/.ssh COPY config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf COPY startup.sh /opt/startup.sh diff --git a/7.1/config/.terminus/config.yml b/7.1/config/.terminus/config.yml new file mode 100644 index 00000000..c7e89515 --- /dev/null +++ b/7.1/config/.terminus/config.yml @@ -0,0 +1,2 @@ +hide_update_message: true +hide_git_mode_warning: true \ No newline at end of file diff --git a/7.1/config/php/zz-php.ini b/7.1/config/php/zz-php.ini index 0b1aff21..dd78b97d 100644 --- a/7.1/config/php/zz-php.ini +++ b/7.1/config/php/zz-php.ini @@ -7,8 +7,10 @@ sendmail_path = /bin/true date.timezone = UTC display_errors = On display_startup_errors = On + +[mail] ; Enable Mailhog integration by default -sendmail_path = '/usr/local/bin/mhsendmail --smtp-addr=mail:1025' +sendmail_path = '/usr/bin/msmtp -t --host=mail --port=1025' ; Extention settings [opcache] diff --git a/7.1/startup.sh b/7.1/startup.sh index f3308350..e445e512 100755 --- a/7.1/startup.sh +++ b/7.1/startup.sh @@ -154,7 +154,7 @@ touch /var/run/cli if [[ -x ${PROJECT_ROOT}/.docksal/services/cli/startup.sh ]]; then echo-debug "Running custom startup script..." # TODO: should we source the script instead? - ${PROJECT_ROOT}/.docksal/services/cli/startup.sh + su -l docker -c "${PROJECT_ROOT}/.docksal/services/cli/startup.sh" if [[ $? == 0 ]]; then echo-debug "Custom startup script executed successfully." else diff --git a/7.2/Dockerfile b/7.2/Dockerfile index 5b1e4632..f2f53672 100644 --- a/7.2/Dockerfile +++ b/7.2/Dockerfile @@ -1,13 +1,3 @@ -# Build mhsendmail -FROM golang:1.8.3 as mhbuild -ENV MHSENDMAIL_VERSION=0.2.0 -RUN set -xe; \ - go get -d github.com/mailhog/mhsendmail; \ - cd /go/src/github.com/mailhog/mhsendmail; \ - git checkout tags/v${MHSENDMAIL_VERSION}; \ - go get github.com/mailhog/mhsendmail - -# Build the image FROM php:7.2-fpm ARG DEBIAN_FRONTEND=noninteractive @@ -41,7 +31,7 @@ ENV LC_ALL en_US.utf8 RUN set -xe; \ sed -i 's/main/main contrib non-free/' /etc/apt/sources.list; \ # blackfire.io repo - curl -fsSL https://packagecloud.io/gpg.key | apt-key add -; \ + curl -fsSL https://packages.blackfire.io/gpg.key | apt-key add -; \ echo "deb https://packages.blackfire.io/debian any main" | tee /etc/apt/sources.list.d/blackfire.list; \ # git-lfs repo curl -fsSL https://packagecloud.io/github/git-lfs/gpgkey | apt-key add -; \ @@ -71,6 +61,7 @@ RUN set -xe; \ # cgi-fcgi binary - used for self-testing (php-fpm) libfcgi-bin \ mc \ + msmtp \ mysql-client \ nano \ openssh-client \ @@ -82,19 +73,22 @@ RUN set -xe; \ sudo \ supervisor \ unzip \ + webp \ zip \ ;\ # Cleanup apt-get clean; rm -rf /var/lib/apt/lists/* RUN set -xe; \ - # Create a regular user/group "docker" (uid = 1000, gid = 1000 ) with access to sudo + # Create a regular user/group "docker" (uid = 1000, gid = 1000 ) groupadd docker -g 1000; \ - useradd -m -s /bin/bash -u 1000 -g 1000 -G sudo -p docker docker; \ + useradd -m -s /bin/bash -u 1000 -g 1000 -p docker docker; \ + # Give the docker user sudo access + usermod -a -G sudo docker; \ echo 'docker ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers ENV GOSU_VERSION=1.10 \ - GOMPLATE_VERSION=2.4.0 + GOMPLATE_VERSION=3.0.0 RUN set -xe; \ # Install gosu and give access to the docker user primary group to use it. # gosu is used instead of sudo to start the main container process (pid 1) in a docker friendly way. @@ -131,15 +125,16 @@ RUN set -xe; \ libldap2-dev \ libmagickcore-dev \ libmagickwand-dev \ - libmcrypt-dev \ libmemcached-dev \ libmhash-dev \ libpng-dev \ libpq-dev \ + libwebp-dev \ libssh2-1-dev \ + libxpm-dev \ libxslt1-dev \ + libzip-dev \ unixodbc-dev \ - zlib1g-dev \ "; \ apt-get update >/dev/null; \ # Necessary for msodbcsql17 (MSSQL) @@ -155,26 +150,30 @@ RUN set -xe; \ libldap-2.4-2 \ libmagickcore-6.q16-3 \ libmagickwand-6.q16-3 \ - libmcrypt4 \ libmemcached11 \ libmemcachedutil2 \ libmhash2 \ libpng16-16 \ libpq5 \ libssh2-1 \ + libxpm4 \ libxslt1.1 \ + libzip4 \ msodbcsql17 \ - zlib1g \ ;\ - # SSH2 must be installed from source + # SSH2 must be installed from source for PHP 7.x git clone https://github.com/php/pecl-networking-ssh2.git /usr/src/php/ext/ssh2 && rm -rf /usr/src/php/ext/ssh2/.git; \ - # XDEBUG must be installed from source - git clone https://github.com/xdebug/xdebug.git /usr/src/php/ext/xdebug && rm -rf /usr/src/php/ext/xdebug/.git; \ \ - docker-php-ext-configure >/dev/null gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/; \ + docker-php-ext-configure >/dev/null gd \ + --with-freetype-dir=/usr/include/ \ + --with-jpeg-dir=/usr/include/ \ + --with-webp-dir=/usr/include/ \ + --with-png-dir=/usr/include/ \ + --with-xpm-dir=/usr/include/; \ docker-php-ext-configure >/dev/null imap --with-kerberos --with-imap-ssl; \ docker-php-ext-configure >/dev/null ldap --with-libdir=lib/x86_64-linux-gnu/; \ docker-php-ext-configure >/dev/null pgsql --with-pgsql=/usr/local/pgsql/; \ + docker-php-ext-configure >/dev/null zip --with-libzip; \ \ docker-php-ext-install >/dev/null -j$(nproc) \ bcmath \ @@ -186,7 +185,8 @@ RUN set -xe; \ imap \ intl \ ldap \ - # mcrypt is removed from 7.2. See Deprecated features + # mcrypt is deprecated in 7.1 and removed in 7.2. See Deprecated features. + # mcrypt \ mysqli \ opcache \ pcntl \ @@ -196,7 +196,6 @@ RUN set -xe; \ soap \ sockets \ ssh2 \ - xdebug \ xsl \ zip \ ;\ @@ -210,6 +209,7 @@ RUN set -xe; \ pdo_sqlsrv \ redis \ sqlsrv \ + xdebug \ ;\ docker-php-ext-enable \ apcu \ @@ -220,8 +220,6 @@ RUN set -xe; \ redis \ sqlsrv \ ;\ - # Disable xdebug by default - rm -f /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini; \ # Cleanup docker-php-source delete; \ rm -rf /tmp/pear ~/.pearrc; \ @@ -229,13 +227,13 @@ RUN set -xe; \ apt-get clean; rm -rf /var/lib/apt/lists/* # PHP tools (installed globally) -ENV COMPOSER_VERSION=1.6.3 \ - DRUSH_VERSION=8.1.16 \ +ENV COMPOSER_VERSION=1.8.0 \ + DRUSH_VERSION=8.1.18 \ DRUSH_LAUNCHER_VERSION=0.6.0 \ - DRUPAL_CONSOLE_LAUNCHER_VERSION=1.7.0 \ - WPCLI_VERSION=1.5.0 \ - BLACKFIRE_VERSION=1.15.0 \ - PLATFORMSH_CLI_VERSION=3.33.5 + DRUPAL_CONSOLE_LAUNCHER_VERSION=1.8.0 \ + WPCLI_VERSION=2.0.1 \ + BLACKFIRE_VERSION=1.22.0 \ + PLATFORMSH_CLI_VERSION=3.38.1 RUN set -xe; \ # Composer curl -fsSL "https://github.com/composer/composer/releases/download/${COMPOSER_VERSION}/composer.phar" -o /usr/local/bin/composer; \ @@ -254,42 +252,31 @@ RUN set -xe; \ # Make all downloaded binaries executable in one shot (cd /usr/local/bin && chmod +x composer drush8 drush drupal wp blackfire platform); -# Ruby -RUN set -xe; \ - apt-get update >/dev/null; \ - apt-get -y --no-install-recommends install >/dev/null \ - ruby-full \ - rlwrap \ - ;\ - # Cleanup - apt-get clean; rm -rf /var/lib/apt/lists/*; \ - # bundler - gem install bundler >/dev/null; \ - # Have bundler install gems locally (./.bundle) by default - echo "\n""export BUNDLE_PATH=.bundle" >> /home/docker/.profile - # All further RUN commands will run as the "docker" user USER docker +SHELL ["/bin/bash", "-c"] # PHP tools (installed as user) -ENV MG_CODEGEN_VERSION=1.10 \ - TERMINUS_VERSION=1.8.1 -RUN set -xe; \ +ENV MG_CODEGEN_VERSION=1.10.2 \ + TERMINUS_VERSION=1.9.0 \ + DRUSH_BACKDROP_VERSION=0.1.0 +# Don't use -x here, as the output may be excessive +RUN set -e; \ \ # Set drush8 as a global fallback for Drush Launcher - echo "\n""export DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8" >> $HOME/.profile; \ + echo -e "\n""export DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8" >> $HOME/.profile; \ # Composer based dependencies # Add composer bin directory to PATH - echo "\n"'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> $HOME/.profile; \ + echo -e "\n"'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> $HOME/.profile; \ # Reload updated PATH from profile to make composer/drush/etc. visible below . $HOME/.profile; \ # Install cgr to use it in-place of `composer global require` composer global require consolidation/cgr >/dev/null; \ # Composer parallel install plugin composer global require hirak/prestissimo >/dev/null; \ - # Drupal Coder w/ a matching version of PHP_CodeSniffer - cgr drupal/coder >/dev/null; \ - phpcs --config-set installed_paths $HOME/.composer/global/drupal/coder/vendor/drupal/coder/coder_sniffer; \ + # Drupal Coder & WP Coding Standards w/ a matching version of PHP_CodeSniffer + cgr drupal/coder wp-coding-standards/wpcs >/dev/null; \ + phpcs --config-set installed_paths "$HOME/.composer/global/drupal/coder/vendor/drupal/coder/coder_sniffer/,$HOME/.composer/global/wp-coding-standards/wpcs/vendor/wp-coding-standards/wpcs/"; \ # Magento2 Code Generator cgr staempfli/magento2-code-generator:${MG_CODEGEN_VERSION} >/dev/null; \ # Terminus @@ -299,14 +286,15 @@ RUN set -xe; \ \ # Drush modules drush dl registry_rebuild --default-major=7 --destination=$HOME/.drush >/dev/null; \ + mkdir $HOME/.drush/backdrop && curl -fsSL "https://github.com/backdrop-contrib/drush/archive/${DRUSH_BACKDROP_VERSION}.tar.gz" | tar xz -C $HOME/.drush/backdrop --strip-components 1; \ drush cc drush # Node.js (installed as user) ENV \ - NVM_VERSION=0.33.11 \ - NODE_VERSION=8.11.3 \ - YARN_VERSION=1.8.0 -# Don't use -x here, as nvm prints too much stuff + NVM_VERSION=0.34.0 \ + NODE_VERSION=10.15.0 \ + YARN_VERSION=1.13.0 +# Don't use -x here, as the output may be excessive RUN set -e; \ # NVM and a defaut Node.js version export PROFILE="$HOME/.profile"; \ @@ -317,12 +305,72 @@ RUN set -e; \ export YARN_PROFILE="$HOME/.profile"; \ curl -fsSL https://yarnpkg.com/install.sh | bash -s -- --version ${YARN_VERSION} >/dev/null +# Ruby (installed as user) +ENV \ + RVM_VERSION_INSTALL=1.29.7 \ + RUBY_VERSION_INSTALL=2.6.0 +# Don't use -x here, as the output may be excessive +RUN set -e; \ + # Public GPG servers are not realiable, so downloading keys from rvm.io instead. + #gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB + # Import and trust rvm keys + # mpapis@gmail.com + curl -sSL https://rvm.io/mpapis.asc | gpg --batch --import -; \ + echo 409B6B1796C275462A1703113804BB82D39DC0E3:6: | gpg --batch --import-ownertrust; \ + # piotr.kuczynski@gmail.com + curl -sSL https://rvm.io/pkuczynski.asc | gpg --batch --import -; \ + echo 7D2BAF1CF37B13E2069D6956105BD0E739499BDB:6: | gpg --batch --import-ownertrust; \ + \ + echo 'rvm_autoupdate_flag=0' >> $HOME/.rvmrc; \ + echo 'rvm_silence_path_mismatch_check_flag=1' >> $HOME/.rvmrc; \ + curl -fsSL https://raw.githubusercontent.com/rvm/rvm/${RVM_VERSION_INSTALL}/binscripts/rvm-installer | bash -s -- --ignore-dotfiles --version ${RVM_VERSION_INSTALL}; \ + { \ + echo ''; \ + echo 'export PATH="$PATH:$HOME/.rvm/bin"'; \ + echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"'; \ + } >> $HOME/.profile; \ + # Reload $HOME/.profile to apply settings for the current shell + . $HOME/.profile; \ + \ + # rvm.io does not currently have ruby binaries for Debian 9, so Ruby is compiled from source, which requires a bunch + # of extra dependencies installed (rvm installs these automatically), which bloat this image: + # rvm/ruby required packages: gawk, automake, bison, libffi-dev, libgdbm-dev, libncurses5-dev, libsqlite3-dev, libtool, libyaml-dev, sqlite3, zlib1g-dev, libgmp-dev, libreadline-dev, libssl-dev + rvm install ruby-${RUBY_VERSION_INSTALL}; \ + rvm use ruby-${RUBY_VERSION_INSTALL} --default; \ + \ + gem install bundler; \ + # Have bundler install gems locally (./.bundle) by default + echo -e "\n"'export BUNDLE_PATH=.bundle' >> $HOME/.profile; \ + \ + rvm cleanup all; \ + rvm gemset globalcache enable + +# Python (installed as user) +ENV \ + PYENV_VERSION_INSTALL=1.2.9 +# PYTHON_VERSION_INSTALL=3.7.0 +RUN set -xe; \ + git clone --depth 1 -b v${PYENV_VERSION_INSTALL} https://github.com/pyenv/pyenv.git $HOME/.pyenv; \ + rm -rf $HOME/.pyenv/.git; \ + { \ + echo ''; \ + echo 'export PYENV_ROOT="$HOME/.pyenv"'; \ + echo 'export PATH="$PYENV_ROOT/bin:$PATH"'; \ + echo 'eval "$(pyenv init -)"'; \ + } >> $HOME/.profile +# Disabled for now - uses too much space (~200MB). +# The upsteam Debian 9 image ships with Python 2.7 out of the box, so we'll stick with that by default. +# # Reload $HOME/.profile to apply settings for the current shell +# . $HOME/.profile; \ +# pyenv install ${PYTHON_VERSION_INSTALL}; \ +# pyenv global ${PYTHON_VERSION_INSTALL} + USER root +SHELL ["/bin/sh", "-c"] -# Copy mhsendmail binary from stage 1 -COPY --from=mhbuild /go/bin/mhsendmail /usr/local/bin/mhsendmail # Copy configs and scripts COPY --chown=docker:docker config/.drush /home/docker/.drush +COPY --chown=docker:docker config/.terminus /home/docker/.terminus COPY --chown=docker:docker config/.ssh /home/docker/.ssh COPY config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf COPY startup.sh /opt/startup.sh diff --git a/7.2/config/.terminus/config.yml b/7.2/config/.terminus/config.yml new file mode 100644 index 00000000..c7e89515 --- /dev/null +++ b/7.2/config/.terminus/config.yml @@ -0,0 +1,2 @@ +hide_update_message: true +hide_git_mode_warning: true \ No newline at end of file diff --git a/7.2/config/php/zz-php.ini b/7.2/config/php/zz-php.ini index 0b1aff21..dd78b97d 100644 --- a/7.2/config/php/zz-php.ini +++ b/7.2/config/php/zz-php.ini @@ -7,8 +7,10 @@ sendmail_path = /bin/true date.timezone = UTC display_errors = On display_startup_errors = On + +[mail] ; Enable Mailhog integration by default -sendmail_path = '/usr/local/bin/mhsendmail --smtp-addr=mail:1025' +sendmail_path = '/usr/bin/msmtp -t --host=mail --port=1025' ; Extention settings [opcache] diff --git a/7.2/startup.sh b/7.2/startup.sh index f3308350..e445e512 100755 --- a/7.2/startup.sh +++ b/7.2/startup.sh @@ -154,7 +154,7 @@ touch /var/run/cli if [[ -x ${PROJECT_ROOT}/.docksal/services/cli/startup.sh ]]; then echo-debug "Running custom startup script..." # TODO: should we source the script instead? - ${PROJECT_ROOT}/.docksal/services/cli/startup.sh + su -l docker -c "${PROJECT_ROOT}/.docksal/services/cli/startup.sh" if [[ $? == 0 ]]; then echo-debug "Custom startup script executed successfully." else diff --git a/7.3/Dockerfile b/7.3/Dockerfile new file mode 100644 index 00000000..97bd6fb8 --- /dev/null +++ b/7.3/Dockerfile @@ -0,0 +1,414 @@ +FROM php:7.3-fpm + +ARG DEBIAN_FRONTEND=noninteractive +ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=1 + +# Prevent services autoload (http://jpetazzo.github.io/2013/10/06/policy-rc-d-do-not-start-services-automatically/) +RUN set -xe; \ + echo '#!/bin/sh\nexit 101' > /usr/sbin/policy-rc.d && chmod +x /usr/sbin/policy-rc.d + +# Install basic packages +RUN set -xe; \ + apt-get update >/dev/null; \ + apt-get -y --no-install-recommends install >/dev/null \ + apt-transport-https \ + # ca-certificates and curl come from upstream + #ca-certificates \ + #curl \ + gnupg \ + locales \ + wget \ + ;\ + # Cleanup + apt-get clean; rm -rf /var/lib/apt/lists/* + +# Set en_US.UTF-8 as the default locale +RUN set -xe; \ + localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 +ENV LC_ALL en_US.utf8 + +# Enable additional repos +RUN set -xe; \ + sed -i 's/main/main contrib non-free/' /etc/apt/sources.list; \ + # blackfire.io repo + curl -fsSL https://packages.blackfire.io/gpg.key | apt-key add -; \ + echo "deb https://packages.blackfire.io/debian any main" | tee /etc/apt/sources.list.d/blackfire.list; \ + # git-lfs repo + curl -fsSL https://packagecloud.io/github/git-lfs/gpgkey | apt-key add -; \ + echo 'deb https://packagecloud.io/github/git-lfs/debian stretch main' | tee /etc/apt/sources.list.d/github_git-lfs.list; \ + echo 'deb-src https://packagecloud.io/github/git-lfs/debian stretch main' | tee -a /etc/apt/sources.list.d/github_git-lfs.list; \ + # MSQSQL repo - msodbcsql17, pecl/sqlsrv and pecl/pdo_sqlsrv (PHP 7.0+ only) + curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | apt-key add -; \ + echo 'deb https://packages.microsoft.com/debian/9/prod stretch main' | tee /etc/apt/sources.list.d/mssql.list; + +# Additional packages +RUN set -xe; \ + # Create man direcotries, otherwise some packages may not install (e.g. postgresql-client) + # This should be a temporary workaround until fixed upstream: https://github.com/debuerreotype/debuerreotype/issues/10 + mkdir -p /usr/share/man/man1 /usr/share/man/man7; \ + apt-get update >/dev/null; \ + apt-get -y --no-install-recommends install >/dev/null \ + cron \ + dnsutils \ + git \ + git-lfs \ + ghostscript \ + # html2text binary - used for self-testing (php-fpm) + html2text \ + imagemagick \ + iputils-ping \ + less \ + # cgi-fcgi binary - used for self-testing (php-fpm) + libfcgi-bin \ + mc \ + msmtp \ + mysql-client \ + nano \ + openssh-client \ + openssh-server \ + postgresql-client \ + procps \ + pv \ + rsync \ + sudo \ + supervisor \ + unzip \ + webp \ + zip \ + ;\ + # Cleanup + apt-get clean; rm -rf /var/lib/apt/lists/* + +RUN set -xe; \ + # Create a regular user/group "docker" (uid = 1000, gid = 1000 ) + groupadd docker -g 1000; \ + useradd -m -s /bin/bash -u 1000 -g 1000 -p docker docker; \ + # Give the docker user sudo access + usermod -a -G sudo docker; \ + echo 'docker ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers + +ENV GOSU_VERSION=1.10 \ + GOMPLATE_VERSION=3.0.0 +RUN set -xe; \ + # Install gosu and give access to the docker user primary group to use it. + # gosu is used instead of sudo to start the main container process (pid 1) in a docker friendly way. + # https://github.com/tianon/gosu + curl -fsSL "https://github.com/tianon/gosu/releases/download/${GOSU_VERSION}/gosu-$(dpkg --print-architecture)" -o /usr/local/bin/gosu; \ + chown root:"$(id -gn docker)" /usr/local/bin/gosu; \ + chmod +sx /usr/local/bin/gosu; \ + # gomplate (to process configuration templates in startup.sh) + curl -fsSL https://github.com/hairyhenderson/gomplate/releases/download/v${GOMPLATE_VERSION}/gomplate_linux-amd64-slim -o /usr/local/bin/gomplate; \ + chmod +x /usr/local/bin/gomplate + +# Configure sshd (for use PHPStorm's remote interpreters and tools integrations) +# http://docs.docker.com/examples/running_ssh_service/ +RUN set -xe; \ + mkdir /var/run/sshd; \ + echo 'docker:docker' | chpasswd; \ + sed -i 's/PermitRootLogin without-password/PermitRootLogin yes/' /etc/ssh/sshd_config; \ + # SSH login fix. Otherwise user is kicked off after login + sed 's@session\s*required\s*pam_loginuid.so@session optional pam_loginuid.so@g' -i /etc/pam.d/sshd; \ + echo "export VISIBLE=now" >> /etc/profile +ENV NOTVISIBLE "in users profile" + +# PHP +RUN set -xe; \ + # Note: essential build tools (g++, gcc, make, etc) are included upstream as persistent packages. + # See https://github.com/docker-library/php/blob/4af0a8734a48ab84ee96de513aabc45418b63dc5/7.2/stretch/fpm/Dockerfile#L18-L37 + buildDeps=" \ + libc-client2007e-dev \ + libfreetype6-dev \ + libgpgme11-dev \ + libicu-dev \ + libjpeg62-turbo-dev \ + libkrb5-dev \ + libldap2-dev \ + libmagickcore-dev \ + libmagickwand-dev \ + libmemcached-dev \ + libmhash-dev \ + libpng-dev \ + libpq-dev \ + libwebp-dev \ + libssh2-1-dev \ + libxpm-dev \ + libxslt1-dev \ + libzip-dev \ + "; \ + apt-get update >/dev/null; \ + # Necessary for msodbcsql17 (MSSQL) + ACCEPT_EULA=Y \ + apt-get -y --no-install-recommends install >/dev/null \ + $buildDeps \ + blackfire-php \ + libc-client2007e \ + libfreetype6 \ + libgpgme11 \ + libicu57 \ + libjpeg62-turbo \ + libldap-2.4-2 \ + libmagickcore-6.q16-3 \ + libmagickwand-6.q16-3 \ + libmemcached11 \ + libmemcachedutil2 \ + libmhash2 \ + libpng16-16 \ + libpq5 \ + libssh2-1 \ + libxpm4 \ + libxslt1.1 \ + libzip4 \ + msodbcsql17 \ + ;\ + # SSH2 must be installed from source for PHP 7.x + git clone https://github.com/php/pecl-networking-ssh2.git /usr/src/php/ext/ssh2 && rm -rf /usr/src/php/ext/ssh2/.git; \ + \ + docker-php-ext-configure >/dev/null gd \ + --with-freetype-dir=/usr/include/ \ + --with-jpeg-dir=/usr/include/ \ + --with-webp-dir=/usr/include/ \ + --with-png-dir=/usr/include/ \ + --with-xpm-dir=/usr/include/; \ + docker-php-ext-configure >/dev/null imap --with-kerberos --with-imap-ssl; \ + docker-php-ext-configure >/dev/null ldap --with-libdir=lib/x86_64-linux-gnu/; \ + docker-php-ext-configure >/dev/null pgsql --with-pgsql=/usr/local/pgsql/; \ + docker-php-ext-configure >/dev/null zip --with-libzip; \ + \ + docker-php-ext-install >/dev/null -j$(nproc) \ + bcmath \ + bz2 \ + calendar\ + exif \ + gd \ + gettext \ + imap \ + intl \ + ldap \ + # mcrypt is deprecated in 7.1 and removed in 7.2. See Deprecated features. + # mcrypt \ + mysqli \ + opcache \ + pcntl \ + pdo_mysql \ + pdo_pgsql \ + pgsql \ + soap \ + sockets \ + ssh2 \ + xsl \ + zip \ + ;\ + pecl update-channels; \ + pecl install >/dev/null /dev/null; \ + apt-get clean; rm -rf /var/lib/apt/lists/* + +# PHP tools (installed globally) +ENV COMPOSER_VERSION=1.8.0 \ + DRUSH_VERSION=8.1.18 \ + DRUSH_LAUNCHER_VERSION=0.6.0 \ + DRUPAL_CONSOLE_LAUNCHER_VERSION=1.8.0 \ + WPCLI_VERSION=2.0.1 \ + BLACKFIRE_VERSION=1.22.0 \ + PLATFORMSH_CLI_VERSION=3.38.1 +RUN set -xe; \ + # Composer + curl -fsSL "https://github.com/composer/composer/releases/download/${COMPOSER_VERSION}/composer.phar" -o /usr/local/bin/composer; \ + # Drush 8 (global fallback) + curl -fsSL "https://github.com/drush-ops/drush/releases/download/${DRUSH_VERSION}/drush.phar" -o /usr/local/bin/drush8; \ + # Drush Launcher + curl -fsSL "https://github.com/drush-ops/drush-launcher/releases/download/${DRUSH_LAUNCHER_VERSION}/drush.phar" -o /usr/local/bin/drush; \ + # Drupal Console Launcher + curl -fsSL "https://github.com/hechoendrupal/drupal-console-launcher/releases/download/${DRUPAL_CONSOLE_LAUNCHER_VERSION}/drupal.phar" -o /usr/local/bin/drupal; \ + # Wordpress CLI + curl -fsSL "https://github.com/wp-cli/wp-cli/releases/download/v${WPCLI_VERSION}/wp-cli-${WPCLI_VERSION}.phar" -o /usr/local/bin/wp; \ + # Blackfire CLI + curl -fsSL "https://packages.blackfire.io/binaries/blackfire-agent/${BLACKFIRE_VERSION}/blackfire-cli-linux_static_amd64" -o /usr/local/bin/blackfire; \ + # Platform.sh CLI + curl -fsSL "https://github.com/platformsh/platformsh-cli/releases/download/v${PLATFORMSH_CLI_VERSION}/platform.phar" -o /usr/local/bin/platform; \ + # Make all downloaded binaries executable in one shot + (cd /usr/local/bin && chmod +x composer drush8 drush drupal wp blackfire platform); + +# All further RUN commands will run as the "docker" user +USER docker +SHELL ["/bin/bash", "-c"] + +# PHP tools (installed as user) +ENV MG_CODEGEN_VERSION=1.10.2 \ + TERMINUS_VERSION=1.9.0 \ + DRUSH_BACKDROP_VERSION=0.1.0 +# Don't use -x here, as the output may be excessive +RUN set -e; \ + \ + # Set drush8 as a global fallback for Drush Launcher + echo -e "\n""export DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8" >> $HOME/.profile; \ + # Composer based dependencies + # Add composer bin directory to PATH + echo -e "\n"'export PATH="$PATH:$HOME/.composer/vendor/bin"' >> $HOME/.profile; \ + # Reload updated PATH from profile to make composer/drush/etc. visible below + . $HOME/.profile; \ + # Install cgr to use it in-place of `composer global require` + composer global require consolidation/cgr >/dev/null; \ + # Composer parallel install plugin + composer global require hirak/prestissimo >/dev/null; \ + # Drupal Coder & WP Coding Standards w/ a matching version of PHP_CodeSniffer + cgr drupal/coder wp-coding-standards/wpcs >/dev/null; \ + phpcs --config-set installed_paths "$HOME/.composer/global/drupal/coder/vendor/drupal/coder/coder_sniffer/,$HOME/.composer/global/wp-coding-standards/wpcs/vendor/wp-coding-standards/wpcs/"; \ + # Magento2 Code Generator + cgr staempfli/magento2-code-generator:${MG_CODEGEN_VERSION} >/dev/null; \ + # Terminus + cgr pantheon-systems/terminus:${TERMINUS_VERSION} >/dev/null; \ + # Cleanup + composer clear-cache; \ + \ + # Drush modules + drush dl registry_rebuild --default-major=7 --destination=$HOME/.drush >/dev/null; \ + mkdir $HOME/.drush/backdrop && curl -fsSL "https://github.com/backdrop-contrib/drush/archive/${DRUSH_BACKDROP_VERSION}.tar.gz" | tar xz -C $HOME/.drush/backdrop --strip-components 1; \ + drush cc drush + +# Node.js (installed as user) +ENV \ + NVM_VERSION=0.34.0 \ + NODE_VERSION=10.15.0 \ + YARN_VERSION=1.13.0 +# Don't use -x here, as the output may be excessive +RUN set -e; \ + # NVM and a defaut Node.js version + export PROFILE="$HOME/.profile"; \ + curl -fsSL https://raw.githubusercontent.com/creationix/nvm/v${NVM_VERSION}/install.sh | bash >/dev/null; \ + # Reload profile to load nvm (needed by Yarn installation below) + . $HOME/.profile; \ + # Yarn + export YARN_PROFILE="$HOME/.profile"; \ + curl -fsSL https://yarnpkg.com/install.sh | bash -s -- --version ${YARN_VERSION} >/dev/null + +# Ruby (installed as user) +ENV \ + RVM_VERSION_INSTALL=1.29.7 \ + RUBY_VERSION_INSTALL=2.6.0 +# Don't use -x here, as the output may be excessive +RUN set -e; \ + # Public GPG servers are not realiable, so downloading keys from rvm.io instead. + #gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB + # Import and trust rvm keys + # mpapis@gmail.com + curl -sSL https://rvm.io/mpapis.asc | gpg --batch --import -; \ + echo 409B6B1796C275462A1703113804BB82D39DC0E3:6: | gpg --batch --import-ownertrust; \ + # piotr.kuczynski@gmail.com + curl -sSL https://rvm.io/pkuczynski.asc | gpg --batch --import -; \ + echo 7D2BAF1CF37B13E2069D6956105BD0E739499BDB:6: | gpg --batch --import-ownertrust; \ + \ + echo 'rvm_autoupdate_flag=0' >> $HOME/.rvmrc; \ + echo 'rvm_silence_path_mismatch_check_flag=1' >> $HOME/.rvmrc; \ + curl -fsSL https://raw.githubusercontent.com/rvm/rvm/${RVM_VERSION_INSTALL}/binscripts/rvm-installer | bash -s -- --ignore-dotfiles --version ${RVM_VERSION_INSTALL}; \ + { \ + echo ''; \ + echo 'export PATH="$PATH:$HOME/.rvm/bin"'; \ + echo '[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"'; \ + } >> $HOME/.profile; \ + # Reload $HOME/.profile to apply settings for the current shell + . $HOME/.profile; \ + \ + # rvm.io does not currently have ruby binaries for Debian 9, so Ruby is compiled from source, which requires a bunch + # of extra dependencies installed (rvm installs these automatically), which bloat this image: + # rvm/ruby required packages: gawk, automake, bison, libffi-dev, libgdbm-dev, libncurses5-dev, libsqlite3-dev, libtool, libyaml-dev, sqlite3, zlib1g-dev, libgmp-dev, libreadline-dev, libssl-dev + rvm install ruby-${RUBY_VERSION_INSTALL}; \ + rvm use ruby-${RUBY_VERSION_INSTALL} --default; \ + \ + gem install bundler; \ + # Have bundler install gems locally (./.bundle) by default + echo -e "\n"'export BUNDLE_PATH=.bundle' >> $HOME/.profile; \ + \ + rvm cleanup all; \ + rvm gemset globalcache enable + +# Python (installed as user) +ENV \ + PYENV_VERSION_INSTALL=1.2.9 +# PYTHON_VERSION_INSTALL=3.7.0 +RUN set -xe; \ + git clone --depth 1 -b v${PYENV_VERSION_INSTALL} https://github.com/pyenv/pyenv.git $HOME/.pyenv; \ + rm -rf $HOME/.pyenv/.git; \ + { \ + echo ''; \ + echo 'export PYENV_ROOT="$HOME/.pyenv"'; \ + echo 'export PATH="$PYENV_ROOT/bin:$PATH"'; \ + echo 'eval "$(pyenv init -)"'; \ + } >> $HOME/.profile +# Disabled for now - uses too much space (~200MB). +# The upsteam Debian 9 image ships with Python 2.7 out of the box, so we'll stick with that by default. +# # Reload $HOME/.profile to apply settings for the current shell +# . $HOME/.profile; \ +# pyenv install ${PYTHON_VERSION_INSTALL}; \ +# pyenv global ${PYTHON_VERSION_INSTALL} + +USER root +SHELL ["/bin/sh", "-c"] + +# Copy configs and scripts +COPY --chown=docker:docker config/.drush /home/docker/.drush +COPY --chown=docker:docker config/.terminus /home/docker/.terminus +COPY --chown=docker:docker config/.ssh /home/docker/.ssh +COPY config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf +COPY startup.sh /opt/startup.sh +COPY healthcheck.sh /opt/healthcheck.sh +# PHP default settings, global overrides and fpm overrides +ADD https://raw.githubusercontent.com/php/php-src/PHP-${PHP_VERSION}/php.ini-development /usr/local/etc/php/php.ini +COPY config/php/zz-php.ini /usr/local/etc/php/conf.d/zz-php.ini +COPY config/php/xdebug.ini /opt/docker-php-ext-xdebug.ini +COPY config/php/zz-php-fpm.conf /usr/local/etc/php-fpm.d/zz-php-fpm.conf +# Create symlinks to project level overrides (if the source files are missing, nothing will break) +RUN set -xe; \ + ln -s /var/www/.docksal/etc/php/php.ini /usr/local/etc/php/conf.d/zzz-php.ini; \ + ln -s /var/www/.docksal/etc/php/php-fpm.conf /usr/local/etc/php-fpm.d/zzz-php-fpm.conf + +ENV \ + # ssh-agent proxy socket (requires docksal/ssh-agent) + SSH_AUTH_SOCK=/.ssh-agent/proxy-socket \ + # Set TERM so text editors/etc. can be used + TERM=xterm \ + # Allow PROJECT_ROOT to be universally used in fin custom commands (inside and outside cli) + PROJECT_ROOT=/var/www \ + # Default values for HOST_UID and HOST_GUI to match the default Ubuntu user. These are used in startup.sh + HOST_UID=1000 \ + HOST_GID=1000 \ + # xdebug disabled by default + XDEBUG_ENABLED=0 + +EXPOSE 9000 +EXPOSE 22 +EXPOSE 3000 + +WORKDIR /var/www + +# Starter script +ENTRYPOINT ["/opt/startup.sh"] + +# By default, launch supervisord to keep the container running. +CMD ["supervisord"] + +# Health check script +HEALTHCHECK --interval=5s --timeout=1s --retries=12 CMD ["/opt/healthcheck.sh"] diff --git a/7.3/Makefile b/7.3/Makefile new file mode 100644 index 00000000..05b65a72 --- /dev/null +++ b/7.3/Makefile @@ -0,0 +1,56 @@ +-include ../tests/env_make +-include env_make + +VERSION ?= 7.3 +REPO = docksal/cli +TAG = build-$(VERSION) +NAME = docksal-cli-$(VERSION) +CWD = $(shell pwd) + +# Improve write performance for /home/docker by turning it into a volume +VOLUMES += -v /home/docker + +.PHONY: build test push shell run start stop logs clean release + +build: + docker build -t $(REPO):$(TAG) . + +test: + IMAGE=$(REPO):$(TAG) NAME=$(NAME) VERSION=$(VERSION) ../tests/test.bats + +push: + docker push $(REPO):$(TAG) + +run: clean + docker run --rm --name $(NAME) -it $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(TAG) + +start: clean + docker run -d --name $(NAME) $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(TAG) + +# Non-interactive and non-tty docker exec (uses LF instead of CRLF line endings) +exec: + @docker exec -u docker $(NAME) bash -lc "$(CMD)" + +# Interactive docker exec +exec-it: + @docker exec -u docker -it $(NAME) bash -ilc "$(CMD)" + +shell: + @docker exec -u docker -it $(NAME) bash -il + +stop: + docker stop $(NAME) + +logs: + docker logs $(NAME) + +logs-follow: + docker logs -f $(NAME) + +clean: + docker rm -vf $(NAME) >/dev/null 2>&1 || true + +release: build + make push -e TAG=$(TAG) + +default: build diff --git a/7.3/config/.drush/acapi.drush.inc b/7.3/config/.drush/acapi.drush.inc new file mode 100644 index 00000000..b04b8a3c --- /dev/null +++ b/7.3/config/.drush/acapi.drush.inc @@ -0,0 +1,1227 @@ + 'Retrieve Drush aliases for all accessible Acquia Cloud sites.', + 'arguments' => array( + ), + 'options' => $options, + ); + + // Most of the commands below use drush_acapi_ac_generic_callback, which + // translates their 'method', 'resource', and 'arguments' properties into + // the appropriate API call, executes it, and displays the result. + + ////////////////////////////////////////////////////////////////////// + // Login + ////////////////////////////////////////////////////////////////////// + $items['ac-api-login'] = array( + 'description' => 'Store Acquia Cloud API credentials and configuration information.', + 'arguments' => array( + ), + 'options' => $options + array( + 'reset' => array( + 'description' => 'Discard any existing stored values from a previous call. Without this option, new values will be merged with existing values.' + ), + ), + ); + + ////////////////////////////////////////////////////////////////////// + // Sites and environments + ////////////////////////////////////////////////////////////////////// + $items['ac-site-list'] = array( + 'description' => 'List all sites available to the current user.', + 'arguments' => array( + ), + 'method' => 'GET', + 'resource' => '/sites', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-site-info'] = array( + 'description' => 'Show information about a site.', + 'arguments' => array( + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-environment-list'] = array( + 'description' => "List a site's environments.", + 'arguments' => array( + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/envs', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-environment-info'] = array( + 'description' => "Show information about a site environment.", + 'arguments' => array( + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/envs/:env', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-environment-install'] = array( + 'description' => "Install a Drupal distribution from a pre-selected list, URL, or Drush Makefile.", + 'arguments' => array( + 'type' => 'Type of distro source: distro_url or make_url.', + 'source' => 'A URL to a distro or URL to a Drush make file.', + ), + 'method' => 'POST', + 'resource' => '/sites/:realm::site/envs/:env/install/:type', + 'params' => array('source'), + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-environment-livedev'] = array( + 'description' => "Configure Live Development on a site environment.", + 'arguments' => array( + 'action' => 'Action to take. \'enable\' or \'disable\' live development.', + 'discard' => 'When action is \'disable\', set to 1 to discard uncommitted changes.', + ), + 'method' => 'POST', + 'resource' => '/sites/:realm::site/envs/:env/livedev/:action', + 'params' => array('discard'), + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + + ////////////////////////////////////////////////////////////////////// + // Servers + ////////////////////////////////////////////////////////////////////// + $items['ac-server-list'] = array( + 'description' => "List servers for a site and environment.", + 'arguments' => array( + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/envs/:env/servers', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-server-info'] = array( + 'description' => "Show information about a server.", + 'arguments' => array( + 'server' => 'Server name.', + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/envs/:env/servers/:server', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-server-php-procs'] = array( + 'description' => "Calculate the php max procs value based on possible memory limits and apc shm settings.", + 'arguments' => array( + 'server' => 'Server name.', + 'memory_limits' => 'Memory limits.', + 'apc_shm' => 'APC shm settings.' + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/envs/:env/servers/:server/php-procs', + 'params_array' => array( + 'memory_limits', + 'apc_shm', + ), + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + + ////////////////////////////////////////////////////////////////////// + // Databases + ////////////////////////////////////////////////////////////////////// + $items['ac-database-list'] = array( + 'description' => "List a site's databases.", + 'arguments' => array( + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/dbs', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-database-info'] = array( + 'description' => "Show information about a site database.", + 'arguments' => array( + 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/dbs/:db', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-database-add'] = array( + 'description' => 'Add a database.', + 'arguments' => array( + 'db' => 'The database.', + ), + 'method' => 'POST', + 'resource' => '/sites/:realm::site/dbs', + 'body_fields' => array('db'), + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-database-delete'] = array( + 'description' => 'Delete a database.', + 'arguments' => array( + 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', + ), + 'method' => 'DELETE', + 'resource' => '/sites/:realm::site/dbs/:db', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-database-instance-list'] = array( + 'description' => "List a site environment's database instances.", + 'arguments' => array( + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/envs/:env/dbs', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-database-instance-info'] = array( + 'description' => "Show information about a site environment's database instance.", + 'arguments' => array( + 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/envs/:env/dbs/:db', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-database-instance-backup-list'] = array( + 'description' => "List a site environment's database instance backups.", + 'arguments' => array( + 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-database-instance-backup-info'] = array( + 'description' => "Show information about a site environment's database instance backup.", + 'arguments' => array( + 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', + 'backup' => 'Backup id.', + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backup', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-database-instance-backup-download'] = array( + 'description' => "Download a site environment database instance backup.", + 'arguments' => array( + 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', + 'backup' => 'Backup id.', + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backup/download', + 'callback' => 'drush_acapi_ac_database_backup_download', + 'options' => array('result-file' => + array( + 'description' => 'Save to a file; specify the full path in which to store the backup. If not provided, the backup is sent the standard output.', + 'example-value' => '/path/to/file', + )) + $options, + ); + $items['ac-database-instance-backup'] = array( + 'description' => 'Create a database instance backup.', + 'arguments' => array( + 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', + ), + 'method' => 'POST', + 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-database-instance-backup-restore'] = array( + 'description' => 'Restore a database instance backup.', + 'arguments' => array( + 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', + 'backupid' => 'The backup id.', + ), + 'method' => 'POST', + 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backupid/restore', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-database-instance-backup-delete'] = array( + 'description' => 'Delete a database instance backup.', + 'arguments' => array( + 'db' => 'The environment-agnostic database name; this is the name shown on the Workflow page of the Cloud UI.', + 'backupid' => 'The backup id.', + ), + 'method' => 'DELETE', + 'resource' => '/sites/:realm::site/envs/:env/dbs/:db/backups/:backupid', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + + ////////////////////////////////////////////////////////////////////// + // Tasks + ////////////////////////////////////////////////////////////////////// + // API resource /sites/:realm::site/tasks returns an array of task records, + // unlike other list resources which return a list of ids, so we need a + // non-standard callback. We'd have to make N API calls to implement this the + // way other data types work, which I think shows that the returning a list of + // records is better. + $items['ac-task-list'] = array( + 'description' => "List a site's tasks.", + 'arguments' => array( + ), + 'options' => array( + 'state' => array( + 'description' => 'The task state to retrieve. If not specified, retrieve all tasks for the site.', + 'example-value' => 'done', + ), + 'days' => array( + 'description' => 'The number of days worth of tasks to retrieve. If not specified, retrieve, at a maximum, 7 days worth of tasks.', + 'example-value' => '5', + ), + 'limit' => array( + 'description' => 'The maximum number of tasks to retrieve. If not specified, retrieve a maximum of 50 tasks. The maximum value allowed is 1000.', + 'example-value' => '500' + ), + ) + $options, + ); + + $items['ac-task-info'] = array( + 'description' => "Show information about a site task.", + 'arguments' => array( + 'task' => 'The task id.', + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/tasks/:task', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + + ////////////////////////////////////////////////////////////////////// + // Workflow + ////////////////////////////////////////////////////////////////////// + $items['ac-code-deploy'] = array( + 'description' => 'Deploy code from one site environment to another.', + 'arguments' => array( + 'target' => 'The target environment.', + ), + 'method' => 'POST', + 'resource' => '/sites/:realm::site/code-deploy/:env/:target', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-code-path-deploy'] = array( + 'description' => 'Deploy a specific branch or tag in an environment.', + 'arguments' => array( + 'path' => 'The branch or tag to deploy.', + ), + 'method' => 'POST', + 'resource' => '/sites/:realm::site/envs/:env/code-deploy', + 'params' => array('path'), + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-database-copy'] = array( + 'description' => 'Copy a database from one site environment to another.', + 'arguments' => array( + 'db' => 'The database.', + 'target' => 'The target environment.', + ), + 'method' => 'POST', + 'resource' => '/sites/:realm::site/dbs/:db/db-copy/:env/:target', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-files-copy'] = array( + 'description' => 'Copy user-uploaded files from one site environment to another.', + 'arguments' => array( + 'target' => 'The target environment.', + ), + 'method' => 'POST', + 'resource' => '/sites/:realm::site/files-copy/:env/:target', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-domain-move'] = array( + 'description' => 'Move a domain from one site environment to another.', + 'arguments' => array( + 'target' => 'The target environment.', + 'domains' => 'Comma separated list of domains, or * for all.', + ), + 'method' => 'POST', + 'resource' => '/sites/:realm::site/domain-move/:env/:target', + 'body_fields_array' => array('domains'), + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + + ////////////////////////////////////////////////////////////////////// + // SSH keys + ////////////////////////////////////////////////////////////////////// + $items['ac-sshkey-list'] = array( + 'description' => "List a site's SSH keys.", + 'arguments' => array( + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/sshkeys', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-sshkey-info'] = array( + 'description' => "Show information about a site SSH key.", + 'arguments' => array( + 'sshkeyid' => 'SSH key id.', + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/sshkeys/:sshkeyid', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-sshkey-add'] = array( + 'description' => 'Add an SSH key to a site.', + 'arguments' => array( + 'ssh_pub_key' => 'File containing the SSH public key.', + 'nickname' => 'The SSH key nickname.', + ), + 'method' => 'POST', + 'resource' => '/sites/:realm::site/sshkeys', + 'params' => array('nickname'), + 'body_fields_path' => array('ssh_pub_key'), + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options + array( + 'shell_access' => + array('description' => 'Set shell access for this key. (true or false)', + 'param' => 'shell_access',), + 'vcs_access' => + array('description' => 'Set git access for this key. (true or false)', + 'param' => 'vcs_access',), + 'blacklist' => + array('description' => 'Array containing a list of environments to disallow access for this key', + 'param' => 'blacklist',), + ), + ); + $items['ac-sshkey-delete'] = array( + 'description' => 'Delete an SSH key from a site.', + 'arguments' => array( + 'sshkeyid' => 'The SSH key id to delete.', + ), + 'method' => 'DELETE', + 'resource' => '/sites/:realm::site/sshkeys/:sshkeyid', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + + ////////////////////////////////////////////////////////////////////// + // SVN users + ////////////////////////////////////////////////////////////////////// + $items['ac-svnuser-list'] = array( + 'description' => "List a site's SVN users.", + 'arguments' => array( + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/svnusers', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-svnuser-info'] = array( + 'description' => "Show information about a site SVN user.", + 'arguments' => array( + 'svnuserid' => 'SVN user id.', + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/svnusers/:svnuserid', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-svnuser-add'] = array( + 'description' => 'Add an SVN user to a site.', + 'arguments' => array( + 'username' => 'SVN username.', + 'password' => 'SVN password.', + ), + 'method' => 'POST', + 'resource' => '/sites/:realm::site/svnusers/:username', + 'body_fields' => array('password'), + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-svnuser-delete'] = array( + 'description' => 'Delete an SVN user from a site.', + 'arguments' => array( + 'svnuserid' => 'The SVN user id to delete.', + ), + 'method' => 'DELETE', + 'resource' => '/sites/:realm::site/svnusers/:svnuserid', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + + ////////////////////////////////////////////////////////////////////// + // Domains + ////////////////////////////////////////////////////////////////////// + $items['ac-domain-list'] = array( + 'description' => "List a site's domains.", + 'arguments' => array( + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/envs/:env/domains', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-domain-info'] = array( + 'description' => "Show information about a site domain.", + 'arguments' => array( + 'domain' => 'Domain name.', + ), + 'method' => 'GET', + 'resource' => '/sites/:realm::site/envs/:env/domains/:domain', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-domain-add'] = array( + 'description' => "Add a domain name to an environment.", + 'arguments' => array( + 'domain' => 'Domain name.', + ), + 'method' => 'POST', + 'resource' => '/sites/:realm::site/envs/:env/domains/:domain', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-domain-delete'] = array( + 'description' => "Delete a domain name from an environment.", + 'arguments' => array( + 'domain' => 'Domain name.', + ), + 'method' => 'DELETE', + 'resource' => '/sites/:realm::site/envs/:env/domains/:domain', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + $items['ac-domain-purge'] = array( + 'description' => "Purge a domain from the Varnish cache.", + 'arguments' => array( + 'domain' => 'Domain name.', + ), + 'method' => 'DELETE', + 'resource' => '/sites/:realm::site/envs/:env/domains/:domain/cache', + 'callback' => 'drush_acapi_ac_generic_callback', + 'options' => $options, + ); + + foreach ($items as $command => $item) { + $items[$command]['orig_options'] = $item['options']; + if (DRUSH_VERSION < 5) { + // Make our command structure work with previous versions of drush while + // still supporting the way we handle arguments, options, and defaults. + $items[$command]['argument-description'] = $item['arguments']; + foreach ($item['options'] as $option => $info) { + $items[$command]['options'][$option] = $info['description']; + } + } + else { + $items[$command]['handle-remote-commands'] = TRUE; + } + + $items[$command]['bootstrap'] = DRUSH_BOOTSTRAP_DRUSH; + $items[$command]['required-arguments'] = TRUE; + } + + return $items; +} + +function acapi_drush_help($section) { + switch ($section) { + case 'meta:acapi:title': + return dt('Acquia commands'); + case 'meta:acapi:summary': + return dt('Acquia Cloud commands.'); + case 'drush:ac-api-login': + $file = '$HOME/.acquia/cloudapi.conf'; + return dt("Store Acquia Cloud API credentials and endpoint information. + +This command stores default email, key, and optionally endpoint values for future Acquia Cloud API commands in @file. File location can be altered with the ac-config option on all Acquia Cloud commands.", array('@file' => $file)); + } +} + +/** + * Write content to a file if the file does not already have that content. + * Log a message when the file is updated. + * + * @param $path + * The full path to update. + * @param $content + * The content to write. + * @return + * TRUE if the file is updated, FALSE otherwise. + */ +function drush_acapi_update_file($path, $content) { + $current = @file_get_contents($path); + if ($current != $content) { + file_put_contents($path, $content); + drush_log(dt('Updated %path.', array('%path' => $path)), 'ok'); + return TRUE; + } + return FALSE; +} + +/** + * Update the Drush aliases for all accessible Cloud sites. + */ +function drush_acapi_acquia_update() { + list($status, $all_aliases) = acapi_call('GET', '/me/drushrc', array(), array(), array(), array('display' => FALSE)); + if ($status == 200) { + foreach ($all_aliases as $realm_site => $aliases) { + list($realm, $site) = explode(':', $realm_site); + $file = _drush_config_file('home.drush', "$site.aliases"); + $content = "'; + + // Preserve existing defaults by loading them unless the user said not to. + $acapi_options = array(); + if (! drush_get_option('reset', FALSE)) { + $acapi_options = acapi_load_options(); + } + + // Collect specified values for each option. + foreach ($defaults as $k => $info) { + // Get the value from the command line, if provided. + $option = drush_get_option($k, NULL); + + if (!isset($option)) { + // No value on cli. The default is the previous value, if any, or what + // the option declaration specifies. + $default = isset($acapi_options[$k]) ? $acapi_options[$k] : $info['default_value']; + + // Prompt for some options, and just use the default for the others. + if (!empty($info['prompt'])) { + // Hide the existing key, if there is one. + if ($info['prompt'][3] && !empty($default)) { + $info['prompt'][1] = $hide_default; + } + else { + $info['prompt'][1] = $default; + } + $option = call_user_func_array('drush_prompt', $info['prompt']); + if ($option == $hide_default) { + $option = $default; + } + } + else { + $option = $default; + } + } + + // Only save non-default values so we can update the defaults without pain. + if ($option == $info['default_value']) { + unset($acapi_options[$k]); + } + else { + $acapi_options[$k] = $option; + } + } + + // We can't store the default path to the config file in the config file. + unset($acapi_options['ac-config']); + + acapi_save_options($acapi_options); +} + +/** + * Save acapi options to the config file. + * + * @param $options + * Hash of options to save. + */ +function acapi_save_options($options) { + $file = acapi_get_option_file(); + $dir = dirname($file); + $dt_args = array('@file' => $file, '@dir' => $dir); + $verbose = drush_get_option('verbose', FALSE); + $simulate = drush_get_option('simulate', FALSE); + if ($verbose || $simulate) { + drush_log(dt('Storing Acquia Cloud API defaults in @file.', $dt_args), 'ok'); + } + if ($verbose) { + drush_print($output); + } + if (! $simulate) { + $output = json_encode($options) . "\n"; + if (!is_dir($dir)) { + if (@mkdir($dir) === FALSE) { + return drush_set_error('ACAPI_CANNOT_WRITE_LOGIN', dt('Cannot create Acquia Cloud API defaults directory @dir.', $dt_args)); + } + } + if (file_put_contents($file, $output) === FALSE) { + return drush_set_error('ACAPI_CANNOT_WRITE_LOGIN', dt('Cannot write to Acquia Cloud API defaults file @file.', $dt_args)); + } + } +} + +/** + * Get saved values for acapi common options from the ac-config file. + * + * @returns + * A hash of saved acapi option names and values. + */ +function acapi_load_options() { + $ret = array(); + $file = acapi_get_option_file(); + $contents = @file_get_contents($file); + if ($contents !== FALSE) { + // Parse old-style Drush PHP config files, and save them in the new format. + if (preg_match('@^<\?php@', $contents) && preg_match_all('@^\$options\[\'acapi\'\]\[\'(\w+)\'\]\s*=\s*\'(.*)\';$@m', $contents, $matches, PREG_SET_ORDER)) { + foreach ($matches as $match) { + $ret[$match[1]] = $match[2]; + } + acapi_save_options($ret); + } + else { + $data = @json_decode($contents, TRUE); + if (is_array($data)) { + $ret = $data; + } + else { + drush_set_error('ACAPI_INVALID_CONF', dt('Acquia Cloud API config file @file is invalid. Run drush ac-api-login.', array('@file' => $file))); + } + } + } + return $ret; +} + +/** + * Return the path for the acapi option file, either --ac-config or + * $HOME/.acquia/cloudapi.conf. + */ +function acapi_get_option_file() { + $defaults = acapi_common_options(); + return drush_get_option('ac-config', $defaults['ac-config']['default_value']); +} + +////////////////////////////////////////////////////////////////////// +// Custom callbacks +////////////////////////////////////////////////////////////////////// + +/** + * List a site's tasks. See the command definition for why this function is + * different. + */ +function drush_acapi_ac_database_backup_download($db, $backupid) { + $command = drush_get_context('command'); + list($site, $env) = acapi_get_site(); + $simulate = drush_get_option('simulate', FALSE); + $result_file = drush_get_option('result-file', ''); + // Similar to drush_sql_build_dump_command(). If the user has set + // $options['result-file'] = TRUE, then we will generate an SQL dump file in + // an automatically-generated backup directory based on site and env values. + if ($result_file === TRUE) { + // User did not pass a specific value for --result-file. Make one. + $backup = drush_include_engine('version_control', 'backup'); + $backup_dir = $backup->prepare_backup_dir($site . '.' . $env); + if (empty($backup_dir)) { + $backup_dir = "/tmp"; + } + $result_file = $backup_dir . '/' . $db . '-' . $backupid .'.sql.gz'; + } + if ($result_file == '') { + $fp = STDOUT; + } + else { + $fp = fopen($result_file, 'w'); + if ($fp == NULL) { + return drush_set_error('ACAPI_ENOENT', dt('Cannot write to result file @result_file.', array('@name' => $result_file))); + } + } + + $api_args = acapi_get_site_args() + array( + ':db' => $db, + ':backup' => $backupid, + ); + list($status, $result) = acapi_call( + $command['method'], + $command['resource'], + $api_args, + array(), + array(), + array('result_stream' => $fp, 'redirect' => 1, 'display' => FALSE) + ); +} + +/** + * List a site's tasks. See the command definition for why this function is + * different. + */ +function drush_acapi_ac_task_list() { + $api_args = acapi_get_site_args(); + $format = acapi_get_option('format'); + $state = drush_get_option('state', NULL); + $days = drush_get_option('days', NULL); + $limit = drush_get_option('limit', NULL); + $params = array(); + if (isset($state)) { + $params['state'] = $state; + } + if (isset($days)) { + $params['days'] = $days; + } + if (isset($limit)) { + $params['limit'] = $limit; + } + + list($status, $result) = acapi_call( + 'GET', + '/sites/:realm::site/tasks', + $api_args, + $params, + array(), + array('display' => !empty($format)) + ); + + $simulate = drush_get_option('simulate', FALSE); + if ($simulate) { + return; + } + + if (empty($format)) { + $display = array(); + foreach ($result as $id => $task) { + $display[$task->id] = $task->description; + } + drush_print_table(drush_key_value_to_array_table($display)); + } +} + +////////////////////////////////////////////////////////////////////// +// Utility functions +////////////////////////////////////////////////////////////////////// + +/** + * Define common options for Acquia Cloud API commands, and their defaults. + */ +function acapi_common_options() { + $options = array( + 'email' => array( + 'description' => 'Email address for your Acquia Network user account', + 'default_value' => '', + 'prompt' => array('Email', NULL, TRUE, FALSE), + 'example-value' => 'example@acquia.com', + ), + 'key' => array( + 'description' => 'Private Cloud API key for your Acquia Network user account', + 'default_value' => '', + 'prompt' => array('Key', NULL, TRUE, TRUE), + 'example-value' => 'apikey', + ), + 'acapi-conf-path' => array( + 'description' => 'Acquia Cloud API config files location. If not specified config will be loaded from $HOME/.drush', + 'default_value' => '', + 'example-value' => '/home/user/acapi-site-configs', + ), + 'ac-config' => array( + 'description' => 'Acquia Cloud API user config file location. If not specified config will be loaded from $HOME', + 'default_value' => drush_server_home() . '/.acquia/cloudapi.conf', + 'example-value' => drush_server_home() . '/.acquia/cloudapi-site-specific.conf', + ), + 'endpoint' => array( + 'description' => 'Acquia Cloud API endpoint URL.', + 'default_value' => 'https://cloudapi.acquia.com/v1', + 'prompt' => array('Endpoint URL', NULL, TRUE, FALSE), + 'example-value' => 'https://cloudapi.acquia.com/v1', + ), + 'cainfo' => array( + 'description' => 'Path to a file containing the SSL certificates needed to verify the ac-api-endpoint.', + 'default_value' => dirname(__FILE__) . '/cloudapi.acquia.com.pem', + 'example-value' => 'cloudapi.acquia.com.pem', + ), + 'format' => array( + 'description' => 'Format to output the object. Use "print_r" for print_r, "export" for var_export, and "json" for JSON. If not provided, the output is printed in a human-readable format.', + 'default_value' => '', + 'example-value' => 'json', + ), + ); + return $options; +} + +/** + * Retrieve an Acquia Cloud API option, in priority order: + * + * - command line + * - ac-config file ($HOME/.acquia/cloudapi.conf by default) + * - per-site acapi file ($HOME/.drush/.acapi.drushrc.php) + * - default from acapi_common_options() + * + * @param $name + * An ac-api option name. + * @return + * The option value, or NULL. + */ +function acapi_get_option($name) { + // Make sure $name is an acapi option. + $options = acapi_common_options(); + if (!isset($options[$name])) { + return drush_set_error('ACAPI_UNKNOWN_OPTION', dt('Unknown ac-api option @name.', array('@name' => $name))); + } + + // If the user specified --$name= on the command line, return . + $value = drush_get_option($name, NULL); + if (isset($value)) { + return $value; + } + + // If the ac-config file sets $name, return the value. + $values = acapi_load_options($name); + if (isset($values[$name])) { + return $values[$name]; + } + + // If $name has a default value, return it. + if (!empty($options[$name]['default_value'])) { + return $options[$name]['default_value']; + } + + // No specified value, no default, return NULL. + return; +} + +/** + * A generic callback for API commands. The command must have: + * + * 'method': $method for acapi_call(). + * 'resource': $resource for acapi_call(). API resource argument names can + * include any argument name from the command's arguments in addition to :site + * and :env which are taken from the site alias. + * + * The command calls acapi_call() with arguments for the specified method, + * resource, and arguments, calling the API and displaying the results. + * + * @return NULL + * This function always returns NULL to avoid invalid JSON. + */ +function drush_acapi_ac_generic_callback() { + $command = drush_get_context('command'); + $api_args = preg_match('@:site@', $command['resource']) ? acapi_get_site_args() : array(); + $params = array(); + $body = array(); + + if (isset($command['default_params'])) { + $params += $command['default_params']; + } + + foreach ($command['argument-description'] as $k => $desc) { + if (isset($command['params']) && array_search($k, $command['params']) !== FALSE) { + $params[$k] = array_shift($command['arguments']); + } + elseif (isset($command['params_array']) && array_search($k, $command['params_array']) !== FALSE) { + $params[$k] = explode(',', array_shift($command['arguments'])); + } + elseif (isset($command['body_fields']) && array_search($k, $command['body_fields']) !== FALSE) { + $body[$k] = array_shift($command['arguments']); + } + elseif (isset($command['body_fields_array']) && array_search($k, $command['body_fields_array']) !== FALSE) { + $body[$k] = explode(',', array_shift($command['arguments'])); + } + elseif (isset($command['body_fields_path']) && array_search($k, $command['body_fields_path']) !== FALSE) { + $path = array_shift($command['arguments']); + $body[$k] = file_get_contents($path); + if ($body[$k] === FALSE) { + drush_set_error('ACAPI_ENOENT', dt('Cannot read @arg path @path.', array('@arg' => $k, '@path' => $path))); + return; + } + } + else { + $api_args[":$k"] = array_shift($command['arguments']); + } + } + + foreach ($command['orig_options'] as $option => $info) { + if (!empty($info['param'])) { + if (drush_get_option($option, FALSE)) { + $params[$info['param']] = $info['value']; + } + } + } + + // acapi_call() will print the results, so returning here would result in + // invalid JSON. + acapi_call($command['method'], $command['resource'], $api_args, $params, $body); +} + +/** + * Return the Acquia Cloud site information specified via the site + * alias. + * + * @param $site_required (TRUE) + * Set an error if site alias options are not found. + * @return + * An array of three elements, site name, environment and realm unless the + * alias file pre-dates the addition of realm. + */ +function acapi_get_site($site_required = TRUE) { + return array_values(acapi_get_site_args($site_required)); +} + +/** + * Get arguments aboud the Acquia Cloud site ready to be used for replacement + * in a URI. + * + * @param $site_required (TRUE) + * Set an error if site alias options are not found. + * @return array + * An associative array containing :site, :env and :realm or an + * empty array if site alias option not found. + */ +function acapi_get_site_args($site_required = TRUE) { + $params = array( + ':site' => drush_get_option('ac-site'), + ':env' => drush_get_option('ac-env'), + ':realm' => drush_get_option('ac-realm'), + ); + + $missing = array_intersect($params, array(NULL)); + if ($missing) { + if ($site_required) { + $missing = str_replace(':', 'ac-', implode(', ', array_keys($missing))); + $error = dt( + 'Alias file is missing Acquia Cloud information: !missing. Be sure to specify a complete Acquia Cloud alias name, such as @mysite.dev.', + array('!missing' => $missing) + ); + drush_set_error('ACAPI_SITE_REQUIRED', $error); + } + return array(); + } + + return $params; +} + +/** + * Call an Acquia Cloud API resource. + * + * @param $method + * The HTTP method; e.g. GET. + * @param $resource + * The API function to call; e.g. /sites/:realm::site. + * @param $args = array() + * An array of argument values for the resource; e.g: array(':site' => + * 'mysite'). + * @params $params = array() + * An array of query parameters to append to the URL. + * @params $body = array() + * An array of parameters to include in the POST body in JSON format. + * @params $options = array() + * An array of options: + * - display (TRUE): whether to output the result to stdout + * - result_stream: open stream to which to write the response body + * - redirect: the maximum number of redirects to allow + */ +function acapi_call($method, $resource, $args, $params = array(), $body = array(), $options = array()) { + $default_options = array( + 'display' => TRUE, + ); + $options = array_merge($default_options, $options); + + $debug = drush_get_option('debug', FALSE); + $verbose = drush_get_option('verbose', FALSE); + $simulate = drush_get_option('simulate', FALSE); + $format = acapi_get_option('format'); + + // Build the API call URL. + $url = acapi_get_option('endpoint'); + $url .= acapi_dt($resource, $args); + $url .= '.json'; + + foreach ($params as $k => $v) { + if (is_array($v)) { + unset($params[$k]); + foreach ($v as $key => $val) { + $params["$k-$key"] = "$k%5B%5D=" . urlencode($val); + } + } + else { + $params[$k] = "$k=" . urlencode($v); + } + } + + $url .= '?' . implode('&', $params); + + $creds = acapi_get_creds(); + if (!$creds) { + return FALSE; + } + + // Build the body. + $json_body = json_encode($body); + + $display = "curl -X $method '$url'"; + if ($debug) { + $display .= " ($creds)"; + } + if ($debug || $verbose || $simulate) { + drush_print($display, 0, STDERR); + if (!empty($body)) { + drush_print(" $json_body", 0, STDERR); + } + } + + if ($simulate) { + return; + } + + $headers = array(); + $ch = curl_init($url); + // Basic request settings + curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); + curl_setopt($ch, CURLOPT_USERAGENT, basename(__FILE__)); + if (!empty($options['result_stream'])) { + curl_setopt($ch, CURLOPT_FILE, $options['result_stream']); + } + else { + curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE); + } + // User authentication + curl_setopt($ch, CURLOPT_HTTPAUTH, TRUE); + curl_setopt($ch, CURLOPT_USERPWD, $creds); + // SSL + curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); + curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, preg_match('@^https:@', acapi_get_option('endpoint'))); + curl_setopt($ch, CURLOPT_CAINFO, acapi_get_option('cainfo')); + // Redirects + if (!empty($options['redirect'])) { + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE); + curl_setopt($ch, CURLOPT_MAXREDIRS, $options['redirect']+1); + } + /* Body + We need to set a Content-Length header even on empty POST requests, or the webserver + will throw a 411 Length Required. + */ + + curl_setopt($ch, CURLOPT_POSTFIELDS, $json_body); + $headers[] = 'Content-Type: application/json;charset=utf-8'; + $headers[] = 'Content-Length: ' . strlen($json_body); + // Headers + if (!empty($headers)) { + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + } + // Debugging + curl_setopt($ch, CURLOPT_VERBOSE, $debug); + // Go + $content = curl_exec($ch); + if (curl_errno($ch) > 0) { + return drush_set_error('ACAPI_CURL_ERROR', dt('Error accessing @url: @err', array('@url' => $url, '@err' => curl_error($ch)))); + } + + $result = json_decode($content); + $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if (!empty($format)) { + drush_print(drush_format($result, NULL, $format)); + } + else if ($options['display']) { + if (is_array($result)) { + foreach ($result as $item) { + if (! is_scalar($item)) { + drush_print_table(drush_key_value_to_array_table(acapi_convert_values($item))); + } + else { + drush_print($item); + } + } + } + else { + if ($method == 'POST') { + // All POST actions return a task. Display something helpful. + drush_log(dt('Task @taskid started.', array('@taskid' => $result->id)), 'ok'); + } + else { + drush_print_table(drush_key_value_to_array_table(acapi_convert_values($result))); + } + } + } + + if ($status != 200) { + return drush_set_error('ACAPI_HTTP_STATUS_' . $status, dt('API status code @status', array('@status' => $status))); + } + + return array($status, $result); +} + +/** + * Return Acquia Cloud API credentials as username:password, or log an error + * if they are unavailable. + */ +function acapi_get_creds() { + $user = acapi_get_option('email'); + $pass = acapi_get_option('key'); + if (empty($user) || empty($pass)) { + return drush_set_error('ACAPI_CREDS_MISSING', dt('Email and api key required; specify --email/--key or run drush ac-api-login')); + } + return "$user:$pass"; +} + +/** + * Convert NULL, array and object values to appropriate string representations + * so they are printed correctly. + */ +function acapi_convert_values($arr) { + foreach ($arr as $k => $v) { + if (!isset($v)) { + $arr->{$k} = ''; + } + elseif (is_array($v) || is_object($v)) { + $arr->{$k} = '...'; + } + } + return (array) $arr; +} + +/** + * dt() wrapper that URL-encodes all substituted parameters that begin with + * a colon (':'). + */ +function acapi_dt($string, $args = array()) { + foreach ($args as $k => $v) { + if ($k[0] == ':') { + $args[$k] = urlencode($v); + } + } + return dt($string, $args); +} diff --git a/7.3/config/.drush/cloudapi.acquia.com.pem b/7.3/config/.drush/cloudapi.acquia.com.pem new file mode 100644 index 00000000..a5da6cb6 --- /dev/null +++ b/7.3/config/.drush/cloudapi.acquia.com.pem @@ -0,0 +1,98 @@ +-----BEGIN CERTIFICATE----- +MIIGvzCCBaegAwIBAgIQD1pdYvA5l5l2ezeWEsVwNTANBgkqhkiG9w0BAQUFADBm +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSUwIwYDVQQDExxEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBDQS0zMB4XDTEyMDgyNzAwMDAwMFoXDTE1MDkwOTEyMDAwMFowfzELMAkGA1UE +BhMCVVMxFjAUBgNVBAgTDU1hc3NhY2h1c2V0dHMxEzARBgNVBAcTCkJ1cmxpbmd0 +b24xEzARBgNVBAoTCkFjcXVpYSBJbmMxFzAVBgNVBAsTDkFjcXVpYSBIb3N0aW5n +MRUwEwYDVQQDDAwqLmFjcXVpYS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw +ggEKAoIBAQCVmcKz54qAbNO9NyP3hA96gzGRORSB9Bz4EOuFM1kfD/gvZoXoqk87 +NC/jPrvPAxNZDJ33IrO08WDWxBVi6UQ7Q9YYFgU1mm0se4qjwld7dtziDnaq2zXc +x4Q+AVfBj92w+RsVDWA2mNtMSaDePqXGzLOz4muUCA5oCtMg2QD+XIEp1yt13nQb +5nW6PbY6kHHviepNX3wj7TdqTLNPdCVcK9BJz2YTjfHtBBxnGxR/m804RL9fYWKz +r6XUbbf+gcKFwMqRI54Qs5cD20jmYnYgXFYwAx6HvOH9Lr969xMq5ePIQkKc66kG +Opie8unjAQRgd6T7lL+zvpaen1UhJruhAgMBAAGjggNOMIIDSjAfBgNVHSMEGDAW +gBRQ6nOJ2yn7EI+e5QEg1N55mUiD9zAdBgNVHQ4EFgQUJuo64qCLWo0Fvi6AsOYG +IiNj+s8wIwYDVR0RBBwwGoIMKi5hY3F1aWEuY29tggphY3F1aWEuY29tMA4GA1Ud +DwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwYQYDVR0f +BFowWDAqoCigJoYkaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL2NhMy1nMjcuY3Js +MCqgKKAmhiRodHRwOi8vY3JsNC5kaWdpY2VydC5jb20vY2EzLWcyNy5jcmwwggHE +BgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwBATCCAaQwOgYIKwYBBQUHAgEWLmh0 +dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5odG0wggFk +BggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBz +ACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBz +ACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBD +AGUAcgB0ACAAQwBQAC8AQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBp +AG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBo +ACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBl +ACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAg +AHIAZQBmAGUAcgBlAG4AYwBlAC4wewYIKwYBBQUHAQEEbzBtMCQGCCsGAQUFBzAB +hhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wRQYIKwYBBQUHMAKGOWh0dHA6Ly9j +YWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VDQS0zLmNy +dDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBBQUAA4IBAQCwbrUX+rSNdiS1ivce +pI3gzzlOG9FjcPPTfoLD/+eiysiTKe7d3Hb9urHZWGuEWWxXRl+6tND3TAt8ONpM +ZCs+nls+qvspG8ApQMZLgak4kb4+CaLMi6ZpQ2JTI89iChonOjI0OP6dVDbGbTvV +a7LKkiZw1xJNYUnhIiqOE1B9Ww3SUUpe1TLmiAYiiYiiuiyBxMz48sARHXnlEbWw +0dHOnU51tvG1zkv9sATYwv3LtIjf9pmXZnS1W0j5Ap0JeFbgFywVi0zSpbix0fzr +pyArWETFKxirR2bCVFH0sVZ2q3/sznguUx1QgH+tese0hqHm2epRORcOi69mcDYi +Cf5v +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIGWDCCBUCgAwIBAgIQCl8RTQNbF5EX0u/UA4w/OzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA4MDQwMjEyMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR +CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv +KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5 +BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf +1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs +zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d +32duXvsCAwEAAaOCAvowggL2MA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w +ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 +LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH +AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy +AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj +AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg +AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ +AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt +AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj +AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl +AHIAZQBuAGMAZQAuMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm +MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSB +hzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGln +aEFzc3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNl +cnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSME +GDAWgBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUB +INTeeZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAB7ipUiebNtTOA/vphoqrOIDQ+2a +vD6OdRvw/S4iWawTwGHi5/rpmc2HCXVUKL9GYNy+USyS8xuRfDEIcOI3ucFbqL2j +CwD7GhX9A61YasXHJJlIR0YxHpLvtF9ONMeQvzHB+LGEhtCcAarfilYGzjrpDq6X +dF3XcZpCdF/ejUN83ulV7WkAywXgemFhM9EZTfkI7qA5xSU1tyvED7Ld8aW3DiTE +JiiNeXf1L/BXunwH1OH8zVowV36GEEfdMR/X/KLCvzB8XSSq6PmuX2p0ws5rs0bY +Ib4p1I5eFdZCSucyb6Sxa1GDWL4/bcf72gMhy2oWGU4K8K2Eyl2Us1p292E= +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- diff --git a/7.3/config/.drush/drushrc.php b/7.3/config/.drush/drushrc.php new file mode 100644 index 00000000..62fa120e --- /dev/null +++ b/7.3/config/.drush/drushrc.php @@ -0,0 +1,319 @@ + TRUE); + +// Prevent drush ssh command from adding a cd to Drupal root before provided command. +# $command_specific['ssh'] = array('cd' => FALSE); + +// Additional folders to search for scripts. +// Separate by : (Unix-based systems) or ; (Windows). +# $command_specific['script']['script-path'] = 'sites/all/scripts:profiles/myprofile/scripts'; + +// Always show release notes when running pm-update or pm-updatecode. +# $command_specific['pm-update'] = array('notes' => TRUE); +# $command_specific['pm-updatecode'] = array('notes' => TRUE); + +// Set a predetermined username and password when using site-install. +# $command_specific['site-install'] = array('account-name' => 'alice', 'account-pass' => 'secret'); + +/** + * Load a drushrc file from the 'drush' folder at the root of the current + * git repository. Example script below by Grayside. Customize as desired. + * @see: http://grayside.org/node/93. + */ +$repo_dir = drush_get_option('root') ? drush_get_option('root') : getcwd(); +if (drush_shell_exec('cd %s && git rev-parse --show-toplevel 2> ' . drush_bit_bucket(), $repo_dir)) { + $output = drush_shell_exec_output(); + $repo_top = $output[0]; + $options['config'] = $repo_top . '/drush/drushrc.php'; + $options['include'] = $repo_top . '/drush/commands'; + $options['alias-path'] = $repo_top . '/drush/aliases'; +} diff --git a/7.3/config/.ssh/config b/7.3/config/.ssh/config new file mode 100644 index 00000000..74320faa --- /dev/null +++ b/7.3/config/.ssh/config @@ -0,0 +1,5 @@ +# Disable remote host key checking and warnings +Host * + StrictHostKeyChecking no + UserKnownHostsFile /dev/null + LogLevel ERROR diff --git a/7.3/config/.ssh/id_rsa.tmpl b/7.3/config/.ssh/id_rsa.tmpl new file mode 100644 index 00000000..8c6c22f1 --- /dev/null +++ b/7.3/config/.ssh/id_rsa.tmpl @@ -0,0 +1 @@ +{{ getenv "SECRET_SSH_PRIVATE_KEY" | base64.Decode }} \ No newline at end of file diff --git a/7.3/config/.terminus/config.yml b/7.3/config/.terminus/config.yml new file mode 100644 index 00000000..c7e89515 --- /dev/null +++ b/7.3/config/.terminus/config.yml @@ -0,0 +1,2 @@ +hide_update_message: true +hide_git_mode_warning: true \ No newline at end of file diff --git a/7.3/config/php/xdebug.ini b/7.3/config/php/xdebug.ini new file mode 100644 index 00000000..0d754766 --- /dev/null +++ b/7.3/config/php/xdebug.ini @@ -0,0 +1,6 @@ +[xdebug] +zend_extension=xdebug.so +xdebug.remote_enable=1 +xdebug.remote_connect_back=1 +xdebug.remote_port=9000 +xdebug.max_nesting_level=256 diff --git a/7.3/config/php/zz-php-fpm.conf b/7.3/config/php/zz-php-fpm.conf new file mode 100644 index 00000000..d4fb5e22 --- /dev/null +++ b/7.3/config/php/zz-php-fpm.conf @@ -0,0 +1,19 @@ +; PHP-FPM settings + +[global] +; This pid file is used for Docker healthcheck +pid = /run/php-fpm.pid + +[www] +user = docker +catch_workers_output = yes +listen = 0.0.0.0:9000 +clear_env = no + +; PHP (FPM) settings +; See zz-php.ini for global (CLI and FPM) PHP settings +php_value[memory_limit] = 256M +php_value[max_execution_time] = 300 +php_value[upload_max_filesize] = 500M +php_value[post_max_size] = 500M +php_value[max_input_vars] = 2000 diff --git a/7.3/config/php/zz-php.ini b/7.3/config/php/zz-php.ini new file mode 100644 index 00000000..dd78b97d --- /dev/null +++ b/7.3/config/php/zz-php.ini @@ -0,0 +1,19 @@ +; PHP global (CLI and FPM) settings +; To override settings for FPM use zz-php-fpm.conf +[php] +memory_limit = 1024M +max_execution_time = 600 +sendmail_path = /bin/true +date.timezone = UTC +display_errors = On +display_startup_errors = On + +[mail] +; Enable Mailhog integration by default +sendmail_path = '/usr/bin/msmtp -t --host=mail --port=1025' + +; Extention settings +[opcache] +opcache.memory_consumption = 128 +[blackfire] +blackfire.agent_socket = 'tcp://blackfire:8707' diff --git a/7.3/config/supervisord.conf b/7.3/config/supervisord.conf new file mode 100644 index 00000000..d7306e21 --- /dev/null +++ b/7.3/config/supervisord.conf @@ -0,0 +1,22 @@ +[supervisord] +nodaemon = true +# debug prints output from all services to stdout/stderr. +# This way logs can be reviewed with docker logs. +# Additionalluy, logs from specific services are forwarded to individual files on disk. +loglevel = debug + +[program:php-fpm] +command = /usr/local/sbin/php-fpm +stdout_logfile = /var/log/supervisor/php-fpm-stdout +stderr_logfile = /var/log/supervisor/php-fpm-stderr + +[program:sshd] +command = /usr/sbin/sshd -D +stdout_logfile = /var/log/supervisor/sshd-stdout +stderr_logfile = /var/log/supervisor/sshd-stderr + +[program:cron] +# Cron will only log to syslog and nothing else... +command = /usr/sbin/cron -f +stdout_logfile = /var/log/supervisor/cron-stdout +stderr_logfile = /var/log/supervisor/cron-stderr diff --git a/7.3/healthcheck.sh b/7.3/healthcheck.sh new file mode 100755 index 00000000..ed3228a4 --- /dev/null +++ b/7.3/healthcheck.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# Initialization phase in startup.sh is complete +[[ ! -f /var/run/cli ]] && exit 1 + +# supervisor services are running +if [[ -f /run/supervisord.pid ]]; then + [[ ! -f /run/php-fpm.pid ]] && exit 1 + [[ ! -f /run/sshd.pid ]] && exit 1 +fi + +exit 0 diff --git a/7.3/php-modules.txt b/7.3/php-modules.txt new file mode 100644 index 00000000..6c84af2e --- /dev/null +++ b/7.3/php-modules.txt @@ -0,0 +1,65 @@ +[PHP Modules] +apcu +bcmath +blackfire +bz2 +calendar +Core +ctype +curl +date +dom +exif +fileinfo +filter +ftp +gd +gettext +gnupg +hash +iconv +imagick +imap +intl +json +ldap +libxml +mbstring +memcached +mysqli +mysqlnd +openssl +pcntl +pcre +PDO +pdo_mysql +pdo_pgsql +pdo_sqlite +pgsql +Phar +posix +readline +redis +Reflection +session +SimpleXML +soap +sockets +sodium +SPL +sqlite3 +ssh2 +standard +tokenizer +xml +xmlreader +xmlwriter +xsl +Zend OPcache +zip +zlib + +[Zend Modules] +Zend OPcache +blackfire + diff --git a/7.3/startup.sh b/7.3/startup.sh new file mode 100755 index 00000000..e445e512 --- /dev/null +++ b/7.3/startup.sh @@ -0,0 +1,173 @@ +#!/bin/bash + +# This script is running as root by default. +# Switching to the docker user can be done via "gosu docker ". + +HOME_DIR='/home/docker' + +DEBUG=${DEBUG:-0} +# Turn debugging ON when cli is started in the service mode +[[ "$1" == "supervisord" ]] && DEBUG=1 +echo-debug () +{ + [[ "$DEBUG" != 0 ]] && echo "$(date +"%F %H:%M:%S") | $@" +} + +uid_gid_reset () +{ + if [[ "$HOST_UID" != "$(id -u docker)" ]] || [[ "$HOST_GID" != "$(id -g docker)" ]]; then + echo-debug "Updating docker user uid/gid to $HOST_UID/$HOST_GID to match the host user uid/gid..." + usermod -u "$HOST_UID" -o docker + groupmod -g "$HOST_GID" -o "$(id -gn docker)" + fi +} + +xdebug_enable () +{ + echo-debug "Enabling xdebug..." + ln -s /opt/docker-php-ext-xdebug.ini /usr/local/etc/php/conf.d/ +} + +add_ssh_key () +{ + echo-debug "Adding a private SSH key from SECRET_SSH_PRIVATE_KEY..." + render_tmpl "$HOME_DIR/.ssh/id_rsa" + chmod 0600 "$HOME_DIR/.ssh/id_rsa" +} + +# Helper function to render configs from go templates using gomplate +render_tmpl () +{ + local file="${1}" + local tmpl="${1}.tmpl" + + if [[ -f "${tmpl}" ]]; then + echo-debug "Rendering template: ${tmpl}..." + gomplate --file "${tmpl}" --out "${file}" + else + echo-debug "Error: Template file not found: ${tmpl}" + return 1 + fi +} + +# Helper function to loop through all environment variables prefixed with SECRET_ and +# convert to the equivalent variable without SECRET. +# Example: SECRET_TERMINUS_TOKEN => TERMINUS_TOKEN. +convert_secrets () +{ + eval 'secrets=(${!SECRET_@})' + for secret_key in "${secrets[@]}"; do + key=${secret_key#SECRET_} + secret_value=${!secret_key} + + # Write new variables to /etc/profile.d/secrets.sh to make them available for all users/sessions + echo "export ${key}=\"${secret_value}\"" | tee -a "/etc/profile.d/secrets.sh" >/dev/null + + # Also export new variables here + # This makes them available in the server/php-fpm environment + eval "export ${key}=${secret_value}" + done +} + +# Acquia Cloud API login +acquia_login () +{ + echo-debug "Authenticating with Acquia..." + # This has to be done using the docker user via su to load the user environment + # Note: Using 'su -l' to initiate a login session and have .profile sourced for the docker user + local command="drush ac-api-login --email='${ACAPI_EMAIL}' --key='${ACAPI_KEY}' --endpoint='https://cloudapi.acquia.com/v1' && drush ac-site-list" + local output=$(su -l docker -c "${command}" 2>&1) + if [[ $? != 0 ]]; then + echo-debug "ERROR: Acquia authentication failed." + echo + echo "$output" + echo + fi +} + +# Pantheon (terminus) login +terminus_login () +{ + echo-debug "Authenticating with Pantheon..." + # This has to be done using the docker user via su to load the user environment + # Note: Using 'su -l' to initiate a login session and have .profile sourced for the docker user + local command="terminus auth:login --machine-token='${TERMINUS_TOKEN}'" + local output=$(su -l docker -c "${command}" 2>&1) + if [[ $? != 0 ]]; then + echo-debug "ERROR: Pantheon authentication failed." + echo + echo "$output" + echo + fi +} + +# Git settings +git_settings () +{ + # These must be run as the docker user + echo-debug "Configuring git..." + gosu docker git config --global user.email "${GIT_USER_EMAIL}" + gosu docker git config --global user.name "${GIT_USER_NAME}" +} + +# Inject a private SSH key if provided +[[ "$SECRET_SSH_PRIVATE_KEY" != "" ]] && add_ssh_key + +# Convert all Environment Variables Prefixed with SECRET_ +convert_secrets + +# Docker user uid/gid mapping to the host user uid/gid +[[ "$HOST_UID" != "" ]] && [[ "$HOST_GID" != "" ]] && uid_gid_reset + +# Enable xdebug +[[ "$XDEBUG_ENABLED" != "" ]] && [[ "$XDEBUG_ENABLED" != "0" ]] && xdebug_enable + +# Make sure permissions are correct (after uid/gid change and COPY operations in Dockerfile) +# To not bloat the image size, permissions on the home folder are reset at runtime. +echo-debug "Resetting permissions on $HOME_DIR and /var/www..." +chown "${HOST_UID:-1000}:${HOST_GID:-1000}" -R "$HOME_DIR" +# Docker resets the project root folder permissions to 0:0 when cli is recreated (e.g. an env variable updated). +# We apply a fix/workaround for this at startup (non-recursive). +chown "${HOST_UID:-1000}:${HOST_GID:-1000}" /var/www + +# These have to happen after the home directory permissions are reset, +# otherwise the docker user may not have write access to /home/docker, where the auth session data is stored. +# Acquia Cloud API config +[[ "$ACAPI_EMAIL" != "" ]] && [[ "$ACAPI_KEY" != "" ]] && acquia_login +# Automatically authenticate with Pantheon if Terminus token is present +[[ "$TERMINUS_TOKEN" != "" ]] && terminus_login + +# If crontab file is found within project add contents to user crontab file. +if [[ -f ${PROJECT_ROOT}/.docksal/services/cli/crontab ]]; then + echo-debug "Loading crontab..." + cat ${PROJECT_ROOT}/.docksal/services/cli/crontab | crontab -u docker - +fi + +# Apply git settings +[[ "$GIT_USER_EMAIL" != "" ]] && [[ "$GIT_USER_NAME" != "" ]] && git_settings + +# Initialization steps completed. Create a pid file to mark the container as healthy +echo-debug "Preliminary initialization completed." +touch /var/run/cli + +# Execute a custom startup script if present +if [[ -x ${PROJECT_ROOT}/.docksal/services/cli/startup.sh ]]; then + echo-debug "Running custom startup script..." + # TODO: should we source the script instead? + su -l docker -c "${PROJECT_ROOT}/.docksal/services/cli/startup.sh" + if [[ $? == 0 ]]; then + echo-debug "Custom startup script executed successfully." + else + echo-debug "ERROR: Custom startup script execution failed." + fi +fi + +# Execute passed CMD arguments +echo-debug "Passing execution to: $*" +# Service mode (run as root) +if [[ "$1" == "supervisord" ]]; then + exec gosu root supervisord -c /etc/supervisor/supervisord.conf +# Command mode (run as docker user) +else + exec gosu docker "$@" +fi diff --git a/Makefile b/Makefile index 8dc9d945..49105c5c 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # Makefile # -DIRS = 5.6 7.0 7.1 7.2 +DIRS = 5.6 7.0 7.1 7.2 7.3 # the sets of directories to do various things in BUILDDIRS = $(DIRS:%=build-%) INSTALLDIRS = $(DIRS:%=install-%) diff --git a/README.md b/README.md index fcfaeaef..6fd43642 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This image(s) is part of the [Docksal](http://docksal.io) image library. ## Features -- php/php-fpm (w/ xdebug), nodejs (via nvm), phyton, ruby +- php/php-fpm (w/ xdebug), nodejs (via nvm), phyton (via pyenv), ruby (via rvm) - Framework specific tools for Drupal, Wordpress, Magento - Miscellaneous cli tools for day to day web development - Hosting provider cli tools (Acquia, Pantheon, Platform.sh) @@ -19,20 +19,20 @@ This image(s) is part of the [Docksal](http://docksal.io) image library. ## Versions and image tag naming convention - Stable versions - - `2.4-php5.6`, `php5.6` - PHP 5.6 - - `2.4-php7.0`, `php7.0` - PHP 7.0 - - `2.4-php7.1`, `php7.1` - PHP 7.1 - - `2.4-php7.2`, `php7.2`, `latest` - PHP 7.2 + - `2.6-php5.6`, `php5.6` - PHP 5.6 (deprecated, will be removed in v2.7) + - `2.6-php7.0`, `php7.0` - PHP 7.0 (deprecated, will be removed in v2.7) + - `2.6-php7.1`, `php7.1` - PHP 7.1 + - `2.6-php7.2`, `php7.2`, `latest` - PHP 7.2 + - `2.6-php7.3`, `php7.3` - PHP 7.3 - Development versions - - `edge-php5.6` - PHP 5.6 - - `edge-php7.0` - PHP 7.0 + - `edge-php5.6` - PHP 5.6 (deprecated, will be removed in v2.7) + - `edge-php7.0` - PHP 7.0 (deprecated, will be removed in v2.7) - `edge-php7.1` - PHP 7.1 - `edge-php7.2` - PHP 7.2 + - `edge-php7.3` - PHP 7.3 -## Supported languages and tools - -### PHP +## PHP - php-fpm && php-cli - xdebug @@ -49,46 +49,81 @@ This image uses the official `php-fpm` images from [Docker Hub](https://hub.dock This means that PHP and all modules are installed from source. Extra modules have to be installed in the same manner (installing them with `apt-get` won't work). -### NodeJS +### Available PHP database drivers + +- SQLite - via `sqlite3`, `pdo_sqlite` +- MySQL - via `mysqli`, `mysqlnd`, `pdo_mysql` +- PostgreSQL - via `pgsql`, `pdo_pgsql` +- MSSQL - via `mssql` and `pdo_dblib` for PHP 5.6; `sqlsrv` and `pdo_sqlsrv` for PHP 7.0, 7.1, 7.2 + + +### Using PHP Xdebug + +Xdebug is disabled by default. + +To enable it, run the image with `XDEBUG_ENABLED=1`: + +```yml +cli +... + environment: + ... + - XDEBUG_ENABLED=1 + ... +``` + +[See docs](https://docs.docksal.io/en/master/tools/xdebug) on using Xdebug for web and cli PHP debugging. + + +## NodeJS - nvm -- node -- npm +- node v10.15.0 w/ npm v6.4.1 (following NodeJS LTS release cycle) - yarn -NodeJS is installed via `nvm` in the docker user's profile inside the image (`/home/docker/.nvm`). +NodeJS is installed via `nvm` in the `docker` user's profile inside the image (`/home/docker/.nvm`). + +This image comes with NodeJS v10.15.0 by default . -This image follows the LTS release cycle for NodeJS, e.g.: +If you need a different version of node, use `nvm` to install it, e.g, `nvm install 11.6.0`. +Then, use `nvm use 11.6.0` to use it in the current session or `nvm alias default 11.6.0` to use it by default. - Latest LTS Version: 8.11.3 (includes npm 5.6.0) - Latest Current Version: 10.7.0 (includes npm 6.1.0) +## Python -If you need a different version of node, use `nvm` to install it, e.g, `nvm install 10.7.0`. +- pyenv +- python 2.7.13 -Then `nvm use 10.7.0` to use it in the current session or `nvm alias default 10.7.0` to use it by default. +This image comes with a system level installed Python version from upstream (Debian 9). -### Python +Additional versions can be installed via `pyenv`, e.g. `pyenv install 3.7.0`. +Then, use `pyenv local 3.7.0` to use it in the current session or `pyenv global 3.7.0` to set is as the default. -- python +Note: additional versions will be installed in the `docker` user's profile inside the image (`/home/docker/.pyenv`). -### Ruby +## Ruby -- ruby +- rvm +- ruby v2.6.0 - gem - bundler -### Other notable tools +Ruby is installed via `rvm` in the `docker` user's profile inside the image (`/home/docker/.rvm`). + +If you need a different version, use `rvm` to install it, e.g, `rvm install 2.5.1`. +Then, `rvm use 2.5.1` to use it in the current session or `rvm --default use 2.5.1` to use it by default. + +## Notable console tools - git with git-lfs - curl, wget - zip, unzip - mysql, pgsql and mssql cli clients -- imagemagick -- mc +- imagemagick, ghostscript +- mc, rsync - mhsendmail - cron -### Hosting provider tools +## Hosting provider tools - Acquia Cloud API drush commands ([Acquia](https://www.acquia.com/)) - terminus ([Pantheon](https://pantheon.io/)) @@ -96,31 +131,6 @@ Then `nvm use 10.7.0` to use it in the current session or `nvm alias default 10. Also, see the [Secrets](#secrets) section below for more information on managing and using your hosting provider keys. -## Available PHP database drivers - -- SQLite - via `sqlite3`, `pdo_sqlite` -- MySQL - via `mysqli`, `mysqlnd`, `pdo_mysql` -- PostgreSQL - via `pgsql`, `pdo_pgsql` -- MSSQL - via `mssql` and `pdo_dblib` for PHP 5.6; `sqlsrv` and `pdo_sqlsrv` for PHP 7.0+ - - -## Using PHP Xdebug - -Xdebug is disabled by default. - -To enable it, run the image with `XDEBUG_ENABLED=1`: - -```yml -cli -... - environment: - ... - - XDEBUG_ENABLED=1 - ... -``` - -[See docs](https://docs.docksal.io/en/master/tools/xdebug) on using Xdebug for web and cli PHP debugging. - ## Customizing startup @@ -201,10 +211,11 @@ GIT_USER_NAME="Docksal CLI" Starting with version 2.3, there is the `ide` flavor of the images, which comes with Cloud9 pre-installed, e.g.: ``` -2.4-php5.6-ide -2.4-php7.0-ide -2.4-php7.1-ide -2.4-php7.2-ide +2.6-php5.6-ide +2.6-php7.0-ide +2.6-php7.1-ide +2.6-php7.2-ide +2.6-php7.3-ide ``` [See docs](https://docs.docksal.io/en/master/tools/cloud9/) for using Cloud 9 in Docksal. diff --git a/cloud9/tests/test.bats b/cloud9/tests/test.bats index cbd9bfbd..adb4fa37 100755 --- a/cloud9/tests/test.bats +++ b/cloud9/tests/test.bats @@ -30,13 +30,10 @@ _healthcheck () fi # If it does, check the status - echo $health_status | grep '"healthy"' >/dev/null 2>&1 + echo ${health_status} | grep '"healthy"' >/dev/null 2>&1 } # Waits for containers to become healthy -# For reasoning why we are not using `depends_on` `condition` see here: -# https://github.com/docksal/docksal/issues/225#issuecomment-306604063 -# TODO: make this universal. Currently hardcoded for cli only. _healthcheck_wait () { # Wait for cli to become ready by watching its health status @@ -47,14 +44,12 @@ _healthcheck_wait () until _healthcheck "$container_name"; do echo "Waiting for $container_name to become ready..." - sleep "$delay"; + sleep ${delay}; # Give the container 30s to become ready elapsed=$((elapsed + delay)) if ((elapsed > timeout)); then - echo-error "$container_name heathcheck failed" \ - "Container did not enter a healthy state within the expected amount of time." \ - "Try ${yellow}fin restart${NC}" + echo "$container_name heathcheck failed" exit 1 fi done @@ -70,7 +65,12 @@ _healthcheck_wait () ### Setup ### make start - _healthcheck_wait + + run _healthcheck_wait + unset output + # This is a dirty hack to get tests to pass on Travis. + # TODO: This should be replaced with a proper Cloud9 healthcheck in Dockerfile + sleep 10 ### Tests ### diff --git a/scripts/docker-push.sh b/scripts/docker-push.sh index 25ca4819..2f83a2de 100755 --- a/scripts/docker-push.sh +++ b/scripts/docker-push.sh @@ -52,29 +52,30 @@ tag_and_push () # Possible docker image tags IMAGE_TAG_EDGE="edge-php${VERSION}" IMAGE_TAG_STABLE="php${VERSION}" -IMAGE_TAG_RELEASE="${TRAVIS_TAG:1:3}-php${VERSION}" +# Major version, e.g. 2-php7.2 +IMAGE_TAG_RELEASE_MAJOR="${TRAVIS_TAG:1:1}-php${VERSION}" +# Major-minor version, e.g. 2.5-php7.2 +IMAGE_TAG_RELEASE_MAJOR_MINOR="${TRAVIS_TAG:1:3}-php${VERSION}" IMAGE_TAG_LATEST="latest" # Skip pull request builds is_pr && exit -# Figure out which docker image tag to use +docker login -u "${DOCKER_USER}" -p "${DOCKER_PASS}" + +# Push images if is_edge; then - IMAGE_TAG=${IMAGE_TAG_EDGE} + tag_and_push ${REPO}:build-${VERSION} ${REPO}:${IMAGE_TAG_EDGE} elif is_stable; then - IMAGE_TAG=${IMAGE_TAG_STABLE} + tag_and_push ${REPO}:build-${VERSION} ${REPO}:${IMAGE_TAG_STABLE} elif is_release; then - IMAGE_TAG=${IMAGE_TAG_RELEASE} + tag_and_push ${REPO}:build-${VERSION} ${REPO}:${IMAGE_TAG_RELEASE_MAJOR} + tag_and_push ${REPO}:build-${VERSION} ${REPO}:${IMAGE_TAG_RELEASE_MAJOR_MINOR} else # Exit if not on develop, master or release tag exit fi -docker login -u "${DOCKER_USER}" -p "${DOCKER_PASS}" - -# Push images -tag_and_push ${REPO}:build-${VERSION} ${REPO}:${IMAGE_TAG} - # Special case for the "latest" tag # Push (base image only) on stable and release builds if is_latest && (is_stable || is_release); then diff --git a/tests/.docksal/services/cli/crontab b/tests/.docksal/services/cli/crontab index 389f5c04..82d24cee 100644 --- a/tests/.docksal/services/cli/crontab +++ b/tests/.docksal/services/cli/crontab @@ -1 +1 @@ -* * * * * echo "The current date is $(date)" >> /tmp/date.txt +* * * * * echo "The current date is $(date)" > /tmp/date.txt diff --git a/tests/.docksal/services/cli/startup.sh b/tests/.docksal/services/cli/startup.sh index 1966ead3..b41d27a9 100755 --- a/tests/.docksal/services/cli/startup.sh +++ b/tests/.docksal/services/cli/startup.sh @@ -1,3 +1,4 @@ #!/bin/bash echo "I ran properly" > /tmp/test-startup.txt +which terminus > /tmp/test-startup-terminus.txt diff --git a/tests/test.bats b/tests/test.bats index 7437ee48..90fcdca2 100755 --- a/tests/test.bats +++ b/tests/test.bats @@ -34,9 +34,6 @@ _healthcheck () } # Waits for containers to become healthy -# For reasoning why we are not using `depends_on` `condition` see here: -# https://github.com/docksal/docksal/issues/225#issuecomment-306604063 -# TODO: make this universal. Currently hardcoded for cli only. _healthcheck_wait () { # Wait for cli to become ready by watching its health status @@ -52,9 +49,7 @@ _healthcheck_wait () # Give the container 30s to become ready elapsed=$((elapsed + delay)) if ((elapsed > timeout)); then - echo-error "$container_name heathcheck failed" \ - "Container did not enter a healthy state within the expected amount of time." \ - "Try ${yellow}fin restart${NC}" + echo "$container_name heathcheck failed" exit 1 fi done @@ -70,7 +65,9 @@ _healthcheck_wait () ### Setup ### make start - _healthcheck_wait + + run _healthcheck_wait + unset output ### Tests ### @@ -122,7 +119,9 @@ _healthcheck_wait () -v $(pwd)/../tests/docroot:/var/www/docroot \ "$IMAGE" docker cp $(pwd)/../tests/scripts "$NAME:/var/www/" - _healthcheck_wait + + run _healthcheck_wait + unset output ### Tests ### @@ -134,7 +133,7 @@ _healthcheck_wait () output=$(echo "$output" | sed -E 's/[[:space:]]{2,}/ => /g') echo "$output" | grep "memory_limit => 256M => 256M" # sendmail_path, being long, gets printed on two lines. We grep the first line only - echo "$output" | grep "sendmail_path => /usr/local/bin/mhsendmail --smtp-addr=mail: /usr/local/bin/mhsendmail --smtp-addr=mail:" + echo "$output" | grep "sendmail_path => /usr/bin/msmtp -t --host=mail -- /usr/bin/msmtp -t --host=mail --" # Cleanup output after each "run" unset output @@ -149,12 +148,17 @@ _healthcheck_wait () echo "$output" | grep "${VERSION}" unset output + # Confirm WebP support enabled for GD + output=$(echo "$phpInfo" | grep "WebP Support") + echo "$output" | grep "enabled" + unset output + output=$(echo "$phpInfo" | grep "memory_limit") echo "$output" | grep "memory_limit => 1024M => 1024M" unset output output=$(echo "$phpInfo" | grep "sendmail_path") - echo "$output" | grep "sendmail_path => /usr/local/bin/mhsendmail --smtp-addr=mail:1025 => /usr/local/bin/mhsendmail --smtp-addr=mail:1025" + echo "$output" | grep "sendmail_path => /usr/bin/msmtp -t --host=mail --port=1025 => /usr/bin/msmtp -t --host=mail --port=1025" unset output # Check PHP modules @@ -173,7 +177,9 @@ _healthcheck_wait () ### Setup ### make start -e ENV='-e XDEBUG_ENABLED=1' - _healthcheck_wait + + run _healthcheck_wait + unset output ### Tests ### @@ -201,7 +207,9 @@ _healthcheck_wait () ### Setup ### make start - _healthcheck_wait + + run _healthcheck_wait + unset output ### Tests ### @@ -257,7 +265,9 @@ _healthcheck_wait () ### Setup ### make start - _healthcheck_wait + + run _healthcheck_wait + unset output ### Tests ### @@ -280,12 +290,64 @@ _healthcheck_wait () make clean } +@test "Check Ruby tools and versions" { + [[ $SKIP == 1 ]] && skip + + ### Setup ### + make start + + run _healthcheck_wait + unset output + + ### Tests ### + + # rvm + run docker exec -u docker "$NAME" bash -lc 'rvm --version 2>&1 | grep "${RVM_VERSION_INSTALL}"' + [[ ${status} == 0 ]] + unset output + + # ruby + run docker exec -u docker "$NAME" bash -lc 'ruby --version | grep "${RUBY_VERSION_INSTALL}"' + [[ ${status} == 0 ]] + unset output + + ### Cleanup ### + make clean +} + +@test "Check Python tools and versions" { + [[ $SKIP == 1 ]] && skip + + ### Setup ### + make start + + run _healthcheck_wait + unset output + + ### Tests ### + + # pyenv + run docker exec -u docker "$NAME" bash -lc 'pyenv --version 2>&1 | grep "${PYENV_VERSION_INSTALL}"' + [[ ${status} == 0 ]] + unset output + + # pyenv + run docker exec -u docker "$NAME" bash -lc 'python --version 2>&1 | grep "${PYTHON_VERSION_INSTALL}"' + [[ ${status} == 0 ]] + unset output + + ### Cleanup ### + make clean +} + @test "Check misc tools and versions" { [[ $SKIP == 1 ]] && skip ### Setup ### make start - _healthcheck_wait + + run _healthcheck_wait + unset output ### Tests ### @@ -294,9 +356,9 @@ _healthcheck_wait () [[ ${status} == 0 ]] unset output - # Check mhsendmail (does not have a flag to report its versions...) - run docker exec -u docker "$NAME" which mhsendmail - echo "$output" | grep "/usr/local/bin/mhsendmail" + # Check msmtp + run docker exec -u docker "$NAME" which msmtp + echo "$output" | grep "/usr/bin/msmtp" unset output ### Cleanup ### @@ -314,7 +376,9 @@ _healthcheck_wait () ### Setup ### make start -e ENV="-e SECRET_SSH_PRIVATE_KEY" - _healthcheck_wait + + run _healthcheck_wait + unset output ### Tests ### @@ -335,12 +399,18 @@ _healthcheck_wait () [[ $SKIP == 1 ]] && skip make start - _healthcheck_wait + + run _healthcheck_wait + unset output run docker exec -u docker "${NAME}" cat /tmp/test-startup.txt [[ ${status} == 0 ]] [[ "${output}" =~ "I ran properly" ]] + run docker exec -u docker "${NAME}" cat /tmp/test-startup-terminus.txt + [[ ${status} == 0 ]] + [[ "${output}" =~ "/home/docker/.composer/vendor/bin/terminus" ]] + ### Cleanup ### make clean } @@ -354,7 +424,9 @@ _healthcheck_wait () ### Setup ### make start -e ENV='-e SECRET_ACAPI_EMAIL -e SECRET_ACAPI_KEY' - _healthcheck_wait + + run _healthcheck_wait + unset output ### Tests ### @@ -392,7 +464,9 @@ _healthcheck_wait () ### Setup ### make start -e ENV='-e SECRET_PLATFORMSH_CLI_TOKEN' - _healthcheck_wait + + run _healthcheck_wait + unset output ### Tests ### @@ -425,7 +499,9 @@ _healthcheck_wait () ### Setup ### make start -e ENV='-e SECRET_TERMINUS_TOKEN' - _healthcheck_wait + + run _healthcheck_wait + unset output ### Tests ### @@ -455,21 +531,15 @@ _healthcheck_wait () ### Setup ### make start - _healthcheck_wait - - ### Tests ### - # Confirm output from cron is working - # Create tmp date file and confirm it's empty - docker exec -u docker "$NAME" bash -lc 'touch /tmp/date.txt' - run docker exec -u docker "$NAME" bash -lc 'cat /tmp/date.txt' - [[ "${output}" == "" ]] + run _healthcheck_wait unset output - # Sleep for 60+1 seconds so cron can run again. - sleep 61 + ### Tests ### - # Confirm cron has ran and file contents has changed + # Give cron 60s to invoke the scheduled test job + sleep 60 + # Confirm cron has run and file contents has changed run docker exec -u docker "$NAME" bash -lc 'tail -1 /tmp/date.txt' [[ "${output}" =~ "The current date is " ]] unset output @@ -483,7 +553,9 @@ _healthcheck_wait () ### Setup ### make start -e ENV='-e GIT_USER_EMAIL=git@example.com -e GIT_USER_NAME="Docksal CLI"' - _healthcheck_wait + + run _healthcheck_wait + unset output ### Tests ### @@ -499,3 +571,47 @@ _healthcheck_wait () ### Cleanup ### make clean } + +@test "PHPCS Coding standards check" { + [[ $SKIP == 1 ]] && skip + + ### Setup ### + make start + + run _healthcheck_wait + unset output + + ### Tests ### + + # Check PHPCS libraries loaded + # Normalize the output from phpcs -i so it's easier to do matches + run docker exec -u docker "$NAME" bash -lc "phpcs -i | sed 's/,//g'" + output="${output} " + [[ "${output}" =~ " Drupal " ]] + [[ "${output}" =~ " DrupalPractice " ]] + [[ "${output}" =~ " WordPress " ]] # Includes WordPress-Core, WordPress-Docs and WordPress-Extra + unset output + + ### Cleanup ### + make clean +} + +@test "Check Drush Backdrop Commands" { + [[ $SKIP == 1 ]] && skip + + ### Setup ### + make start + + run _healthcheck_wait + unset output + + ### Tests ### + + # Check Drush Backdrop command loaded + run docker exec -u docker "$NAME" bash -lc 'drush help backdrop-core-status' + [[ "${output}" =~ "Provides a birds-eye view of the current Backdrop installation, if any." ]] + unset output + + ### Cleanup ### + make clean +}