diff --git a/.travis.yml b/.travis.yml index eddcabd3..b88586fe 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ env: global: - REPO=docksal/cli - LATEST_VERSION=7.2 + - DOCKSAL_VERSION=develop matrix: - VERSION=5.6 - VERSION=7.0 @@ -18,33 +19,23 @@ before_install: - sudo apt-get install libfcgi0ldbl # cgi-fcgi binary used in tests install: - - curl -fsSL https://get.docksal.io | sh + # Install Docksal to have a matching versions of Docker on the build host + - curl -fsSL https://get.docksal.io | DOCKSAL_VERSION=${DOCKSAL_VERSION} bash - fin version - fin sysinfo script: - - cd ${VERSION} + # Build the base image + - cd ${TRAVIS_BUILD_DIR}/${VERSION} - travis_retry make && make test # Retry builds, as pecl.php.net tends to time out often + # Build the Cloud9 flavor + - cd ${TRAVIS_BUILD_DIR}/cloud9 + - travis_retry make && make test -after_success: | - if [[ "${TRAVIS_PULL_REQUEST}" == "false" ]]; then - [[ "${TRAVIS_BRANCH}" == "develop" ]] && TAG="edge-php${VERSION}" - [[ "${TRAVIS_BRANCH}" == "master" ]] && TAG="php${VERSION}" - [[ "${TRAVIS_TAG}" != "" ]] && TAG="${TRAVIS_TAG:1:3}-php${VERSION}" - - if [[ "$TAG" != "" ]]; then - docker login -u "${DOCKER_USER}" -p "${DOCKER_PASS}" - # Push edge, stable and release tags - docker tag ${REPO}:${VERSION} ${REPO}:${TAG} - docker push ${REPO}:${TAG} - - # Push "latest" tag - if [[ "${TRAVIS_BRANCH}" == "master" ]] && [[ "${VERSION}" == "${LATEST_VERSION}" ]]; then - docker tag ${REPO}:${VERSION} ${REPO}:latest - docker push ${REPO}:latest - fi - fi - fi +after_success: + - docker image ls + - ${TRAVIS_BUILD_DIR}/scripts/docker-push.sh after_failure: - - make logs + - cd ${TRAVIS_BUILD_DIR}/${VERSION} && make logs + - cd ${TRAVIS_BUILD_DIR}/cloud9 && make logs diff --git a/5.6/Dockerfile b/5.6/Dockerfile index 2a6d61d7..0a0f9da0 100644 --- a/5.6/Dockerfile +++ b/5.6/Dockerfile @@ -11,6 +11,7 @@ RUN set -xe; \ FROM php:5.6-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; \ @@ -18,7 +19,8 @@ RUN set -xe; \ # Install basic packages RUN set -xe; \ - apt-get update >/dev/null; apt-get -y --force-yes --no-install-recommends install >/dev/null \ + apt-get update >/dev/null; \ + apt-get -y --no-install-recommends install >/dev/null \ apt-transport-https \ ca-certificates \ curl \ @@ -42,22 +44,20 @@ RUN set -xe; \ # backports repo echo "deb http://ftp.debian.org/debian jessie-backports main" | tee /etc/apt/sources.list.d/backports.list; \ # blackfire.io repo - curl -sSL https://packagecloud.io/gpg.key | apt-key add -; \ + curl -fsSL https://packagecloud.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 -sSL https://packagecloud.io/github/git-lfs/gpgkey | apt-key add -; \ + curl -fsSL https://packagecloud.io/github/git-lfs/gpgkey | apt-key add -; \ echo 'deb https://packagecloud.io/github/git-lfs/debian/ jessie main' | tee /etc/apt/sources.list.d/github_git-lfs.list; \ - echo 'deb-src https://packagecloud.io/github/git-lfs/debian/ jessie main' | tee -a /etc/apt/sources.list.d/github_git-lfs.list; \ - # yarn repo - curl -sSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -; \ - echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list + echo 'deb-src https://packagecloud.io/github/git-lfs/debian/ jessie main' | tee -a /etc/apt/sources.list.d/github_git-lfs.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 --force-yes --no-install-recommends install >/dev/null \ + apt-get update >/dev/null; \ + apt-get -y --no-install-recommends install >/dev/null \ cron \ dnsutils \ git-lfs \ @@ -82,10 +82,9 @@ RUN set -xe; \ unzip \ zip \ zsh \ - yarn \ ;\ # More recent version of git to get composer's git cache. - apt-get -y --force-yes --no-install-recommends -t jessie-backports install git >/dev/null ;\ + apt-get -y --no-install-recommends -t jessie-backports install git >/dev/null ;\ # Cleanup apt-get clean; rm -rf /var/lib/apt/lists/* @@ -95,13 +94,18 @@ RUN set -xe; \ useradd -m -s /bin/bash -u 1000 -g 1000 -G sudo -p docker docker; \ echo 'docker ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers -# 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 +ENV GOSU_VERSION=1.10 \ + GOMPLATE_VERSION=2.4.0 RUN set -xe; \ - curl -sSL "https://github.com/tianon/gosu/releases/download/1.10/gosu-$(dpkg --print-architecture)" -o /usr/local/bin/gosu; \ + # 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 + 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/ @@ -114,7 +118,7 @@ RUN set -xe; \ echo "export VISIBLE=now" >> /etc/profile ENV NOTVISIBLE "in users profile" -# Install PHP extentions +# PHP RUN set -xe; \ buildDeps=" \ freetds-dev \ @@ -137,7 +141,8 @@ RUN set -xe; \ libxslt1-dev \ zlib1g-dev \ "; \ - apt-get update >/dev/null; apt-get -y --force-yes --no-install-recommends install >/dev/null \ + apt-get update >/dev/null; \ + apt-get -y --no-install-recommends install >/dev/null \ $buildDeps \ blackfire-php \ libc-client2007e \ @@ -214,102 +219,100 @@ RUN set -xe; \ apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $buildDeps >/dev/null; \ apt-get clean; rm -rf /var/lib/apt/lists/* -# Copy mhsendmail binary from stage 1 -COPY --from=mhbuild /go/bin/mhsendmail /usr/local/bin/mhsendmail - +# PHP tools (installed globally) ENV COMPOSER_VERSION=1.6.3 \ DRUSH_VERSION=8.1.16 \ DRUSH_LAUNCHER_VERSION=0.6.0 \ - DRUPAL_CONSOLE_VERSION=1.7.0 \ + DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8 \ + DRUPAL_CONSOLE_LAUNCHER_VERSION=1.7.0 \ WPCLI_VERSION=1.5.0 \ - MG_CODEGEN_VERSION=1.10 \ BLACKFIRE_VERSION=1.15.0 \ - GOMPLATE_VERSION=2.4.0 + PLATFORMSH_CLI_VERSION=3.33.5 RUN set -xe; \ # Composer - curl -sSL "https://github.com/composer/composer/releases/download/${COMPOSER_VERSION}/composer.phar" -o /usr/local/bin/composer; \ - # Drush 8 (default) - curl -sSL "https://github.com/drush-ops/drush/releases/download/${DRUSH_VERSION}/drush.phar" -o /usr/local/bin/drush8; \ + 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 -sSL "https://github.com/drush-ops/drush-launcher/releases/download/${DRUSH_LAUNCHER_VERSION}/drush.phar" -o /usr/local/bin/drush; \ - # Drupal Console - curl -sSL "https://github.com/hechoendrupal/drupal-console-launcher/releases/download/${DRUPAL_CONSOLE_VERSION}/drupal.phar" -o /usr/local/bin/drupal; \ + 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 -sSL "https://github.com/wp-cli/wp-cli/releases/download/v${WPCLI_VERSION}/wp-cli-${WPCLI_VERSION}.phar" -o /usr/local/bin/wp; \ - # Magento2 Code Generator - curl -sSL "https://github.com/staempfli/magento2-code-generator/releases/download/${MG_CODEGEN_VERSION}/mg2-codegen.phar" -o /usr/local/bin/mg2-codegen; \ + 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 -sSL https://packages.blackfire.io/binaries/blackfire-agent/${BLACKFIRE_VERSION}/blackfire-cli-linux_static_amd64 -o /usr/local/bin/blackfire; \ - # gomplate - curl -sSL https://github.com/hairyhenderson/gomplate/releases/download/v${GOMPLATE_VERSION}/gomplate_linux-amd64-slim -o /usr/local/bin/gomplate; \ - # Make all binaries executable in one shot - chmod +x /usr/local/bin/* + 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); -# Other language packages and dependencies +# Ruby RUN set -xe; \ - apt-get update >/dev/null; apt-get -y --force-yes --no-install-recommends install >/dev/null \ + apt-get update >/dev/null; \ + apt-get -y --no-install-recommends install >/dev/null \ ruby-full \ - rlwrap; \ - #build-essential; \ + rlwrap \ + ;\ # Cleanup - apt-get clean; rm -rf /var/lib/apt/lists/* - -# bundler -RUN gem install bundler >/dev/null + apt-get clean; rm -rf /var/lib/apt/lists/*; \ + # bundler + gem install bundler >/dev/null # Home directory for bundle installs ENV BUNDLE_PATH .bundler # All further RUN commands will run as the "docker" user USER docker -ENV HOME /home/docker +ARG HOME=/home/docker -# Install nvm and a default node version -ENV NVM_VERSION=0.33.8 \ - NODE_VERSION=8.11.0 \ - NVM_DIR=$HOME/.nvm -# Don't use -x here - node/nvm stuff prints just too much stuff -RUN set -e; \ - curl -sSL https://raw.githubusercontent.com/creationix/nvm/v${NVM_VERSION}/install.sh | bash; \ - . $NVM_DIR/nvm.sh; \ - nvm install $NODE_VERSION >/dev/null; \ - nvm alias default $NODE_VERSION; \ - # Install global node packages - npm install -g npm >/dev/null; \ - # Cleanup - nvm clear-cache && npm cache clear --force; \ - # Fix npm complaining about permissions and not being able to update - sudo rm -rf $HOME/.config - -# Install Composer based dependencies +# PHP tools (installed as user) ENV PATH=$PATH:$HOME/.composer/vendor/bin \ - DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8 -ENV PATH=$PATH:$HOME/.terminus/vendor/bin \ - TERMINUS_VERSION=1.8.0 + MG_CODEGEN_VERSION=1.10 \ + TERMINUS_VERSION=1.8.1 RUN set -xe; \ + \ + # Composer based dependencies # Add composer bin directory to PATH echo "\n"'PATH="$PATH:$HOME/.composer/vendor/bin"' >> $HOME/.profile; \ - # Drush modules - drush dl registry_rebuild --default-major=7 --destination=$HOME/.drush >/dev/null; \ - drush cc drush; \ - # Drupal Coder w/ a matching version of PHP_CodeSniffer - composer global require drupal/coder >/dev/null; \ - phpcs --config-set installed_paths $HOME/.composer/vendor/drupal/coder/coder_sniffer; \ + # 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/vendor/drupal/coder/coder_sniffer; \ + # Magento2 Code Generator + cgr staempfli/magento2-code-generator:${MG_CODEGEN_VERSION} >/dev/null; \ # Terminus - # Installed in a dedicated directory to avoid dependency conflicts. - echo "\n"'PATH="$PATH:$HOME/.terminus/vendor/bin"' >> $HOME/.profile; \ - mkdir -p $HOME/.terminus; \ - # Run in a subshell since we are doing directory switching - (cd $HOME/.terminus && composer require pantheon-systems/terminus:${TERMINUS_VERSION}); \ + cgr pantheon-systems/terminus:${TERMINUS_VERSION} >/dev/null; \ # Cleanup - composer clear-cache + composer clear-cache; \ + \ + # Drush modules + drush dl registry_rebuild --default-major=7 --destination=$HOME/.drush >/dev/null; \ + 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 +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 USER root +# 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/.acquia $HOME/.acquia -COPY --chown=docker:docker config/.docksalrc $HOME/.docksalrc COPY --chown=docker:docker config/.drush $HOME/.drush COPY --chown=docker:docker config/.ssh $HOME/.ssh COPY config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf diff --git a/5.6/Makefile b/5.6/Makefile index b7e6d79d..57347a60 100644 --- a/5.6/Makefile +++ b/5.6/Makefile @@ -1,10 +1,11 @@ +-include ../tests/env_make -include env_make -PHP_VERSION = 5.6 -VERSION ?= php-fpm-$(PHP_VERSION) - +VERSION ?= 5.6 REPO = docksal/cli -NAME = docksal-cli-$(PHP_VERSION) +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 @@ -12,25 +13,30 @@ VOLUMES += -v /home/docker .PHONY: build test push shell run start stop logs clean release build: - docker build -t $(REPO):$(VERSION) . + docker build -t $(REPO):$(TAG) . test: - IMAGE=$(REPO):$(VERSION) NAME=$(NAME) PHP_VERSION=$(PHP_VERSION) ../tests/test.bats + IMAGE=$(REPO):$(TAG) NAME=$(NAME) VERSION=$(VERSION) ../tests/test.bats push: - docker push $(REPO):$(VERSION) - -shell: - docker run --rm --name $(NAME) -it $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(VERSION) /bin/bash + docker push $(REPO):$(TAG) -run: - docker run --rm --name $(NAME) -it $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(VERSION) +run: clean + docker run --rm --name $(NAME) -it $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(TAG) -start: - docker run -d --name $(NAME) $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(VERSION) +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 -it $(NAME) /bin/bash + @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) @@ -38,10 +44,13 @@ stop: logs: docker logs $(NAME) +logs-follow: + docker logs -f $(NAME) + clean: - docker rm -f $(NAME) + docker rm -vf $(NAME) >/dev/null 2>&1 || true release: build - make push -e VERSION=$(VERSION) + make push -e TAG=$(TAG) default: build diff --git a/5.6/config/.docksalrc b/5.6/config/.docksalrc deleted file mode 100644 index 576167b7..00000000 --- a/5.6/config/.docksalrc +++ /dev/null @@ -1,13 +0,0 @@ -# Commands in this file will be sourced for both interactive and non-interactive sessions. - -# Allow alias expansion in non-interactive shells. -shopt -s expand_aliases - -# NVM initialization. -export NVM_DIR="/home/docker/.nvm" -[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm - -# Source alias definitions. -if [ -f ~/.bash_aliases ]; then - . ~/.bash_aliases -fi diff --git a/5.6/config/php/zz-php.ini b/5.6/config/php/zz-php.ini index d521ad93..cc51d5c0 100644 --- a/5.6/config/php/zz-php.ini +++ b/5.6/config/php/zz-php.ini @@ -1,5 +1,5 @@ ; PHP global (CLI and FPM) settings -; To override settings for FPM use docksal-www.conf +; To override settings for FPM use zz-php-fpm.conf [php] memory_limit = 1024M max_execution_time = 600 diff --git a/5.6/config/supervisord.conf b/5.6/config/supervisord.conf index 32b078b2..d7306e21 100644 --- a/5.6/config/supervisord.conf +++ b/5.6/config/supervisord.conf @@ -1,37 +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 -# ---------------------------------------------------------------------------------------------------- -# Optional stuff to make supervisord complain less about misc things not being configured -logfile = /var/log/supervisor/supervisord.log -pidfile = /var/run/supervisord.pid - -[unix_http_server] -file = /var/run/supervisord.sock -chmod = 0700 -username = dummy -password = dummy - -[supervisorctl] -serverurl = unix:///var/run/supervisord.sock -username = dummy -password = dummy - -[rpcinterface:supervisor] -supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface -# END: Optional stuff to make supervisord complain less about misc things not being configured -# ---------------------------------------------------------------------------------------------------- [program:php-fpm] command = /usr/local/sbin/php-fpm -stdout_logfile = /dev/stdout -stdout_logfile_maxbytes = 0 -stderr_logfile = /dev/stderr -stderr_logfile_maxbytes = 0 +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 = /dev/stdout -stdout_logfile_maxbytes = 0 -stderr_logfile = /dev/stderr -stderr_logfile_maxbytes = 0 +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/5.6/startup.sh b/5.6/startup.sh index 9a577e6b..7a615cae 100755 --- a/5.6/startup.sh +++ b/5.6/startup.sh @@ -43,10 +43,40 @@ render_tmpl () 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 +} + +# Pantheon authentication terminus_login () { echo-debug "Authenticating with Pantheon..." - terminus auth:login --machine-token="$SECRET_TERMINUS_TOKEN" >/dev/null 2>&1 + # This has to be done using the docker user via su to load the user environment + # Note: 'su -' = 'su -l' = 'su --login' + local output + output=$(sudo su - docker -c "terminus auth:login --machine-token='${TERMINUS_TOKEN}'" 2>&1) + #>/dev/null 2>&1 + if [[ $? != 0 ]]; then + echo-debug "ERROR: Pantheon authentication failed." + echo + echo "$output" + echo + fi } # Process templates @@ -56,8 +86,8 @@ chmod 0600 "$HOME_DIR/.ssh/id_rsa" # Acquia Cloud API config render_tmpl "$HOME_DIR/.acquia/cloudapi.conf" -# Terminus authentication -[[ "$SECRET_TERMINUS_TOKEN" ]] && terminus_login +# 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 @@ -68,25 +98,44 @@ render_tmpl "$HOME_DIR/.acquia/cloudapi.conf" # 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:$HOST_GID" -R "$HOME_DIR" +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. -chown "$HOST_UID:$HOST_GID" /var/www +# We apply a fix/workaround for this at startup (non-recursive). +chown "${HOST_UID-:1000}:${HOST_GID:-1000}" /var/www + +# Automatically authenticate with Pantheon in Terminus token is present +# Note: this has to happen after th home directory permissions are reset, +# otherwise the docker user may not have write access to /home/.terminus, where the auth session data is stored. +[[ "$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 # Initialization steps completed. Create a pid file to mark the container as healthy -echo-debug "Preliminary initialization completed" +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? + ${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 "Executing the requested command..." +echo-debug "Passing execution to: $*" # Service mode (run as root) if [[ "$1" == "supervisord" ]]; then - exec gosu root supervisord -c /etc/supervisor/conf.d/supervisord.conf + exec gosu root supervisord -c /etc/supervisor/supervisord.conf # Command mode (run as docker user) else - # This makes sure the environment is set up correctly for the docker user - DOCKSALRC='source $HOME/.docksalrc >/dev/null 2>&1' - # Launch the passed command in an non-interactive bash session under docker user - # $@ does not work here. $* has to be used. - exec gosu docker bash -c "$DOCKSALRC; exec $*" + exec gosu docker "$@" fi diff --git a/7.0/Dockerfile b/7.0/Dockerfile index ae314a24..7280bd0b 100644 --- a/7.0/Dockerfile +++ b/7.0/Dockerfile @@ -11,6 +11,7 @@ RUN set -xe; \ FROM php:7.0-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; \ @@ -18,7 +19,8 @@ RUN set -xe; \ # Install basic packages RUN set -xe; \ - apt-get update >/dev/null; apt-get -y --force-yes --no-install-recommends install >/dev/null \ + apt-get update >/dev/null; \ + apt-get -y --no-install-recommends install >/dev/null \ apt-transport-https \ ca-certificates \ curl \ @@ -42,17 +44,14 @@ RUN set -xe; \ # backports repo echo "deb http://ftp.debian.org/debian jessie-backports main" | tee /etc/apt/sources.list.d/backports.list; \ # blackfire.io repo - curl -sSL https://packagecloud.io/gpg.key | apt-key add -; \ + curl -fsSL https://packagecloud.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 -sSL https://packagecloud.io/github/git-lfs/gpgkey | apt-key add -; \ + curl -fsSL https://packagecloud.io/github/git-lfs/gpgkey | apt-key add -; \ echo 'deb https://packagecloud.io/github/git-lfs/debian/ jessie main' | tee /etc/apt/sources.list.d/github_git-lfs.list; \ echo 'deb-src https://packagecloud.io/github/git-lfs/debian/ jessie main' | tee -a /etc/apt/sources.list.d/github_git-lfs.list; \ - # yarn repo - curl -sSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -; \ - echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list; \ # MSQSQL repo - msodbcsql17, pecl/sqlsrv and pecl/pdo_sqlsrv (PHP 7.0+ only) - curl -sSL https://packages.microsoft.com/keys/microsoft.asc | apt-key add -; \ + curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | apt-key add -; \ echo 'deb https://packages.microsoft.com/debian/8/prod jessie main' | tee /etc/apt/sources.list.d/mssql.list; # Additional packages @@ -60,7 +59,8 @@ 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 --force-yes --no-install-recommends install >/dev/null \ + apt-get update >/dev/null; \ + apt-get -y --no-install-recommends install >/dev/null \ cron \ dnsutils \ git-lfs \ @@ -85,10 +85,9 @@ RUN set -xe; \ unzip \ zip \ zsh \ - yarn \ ;\ # More recent version of git to get composer's git cache. - apt-get -y --force-yes --no-install-recommends -t jessie-backports install git >/dev/null ;\ + apt-get -y --no-install-recommends -t jessie-backports install git >/dev/null ;\ # Cleanup apt-get clean; rm -rf /var/lib/apt/lists/* @@ -98,13 +97,18 @@ RUN set -xe; \ useradd -m -s /bin/bash -u 1000 -g 1000 -G sudo -p docker docker; \ echo 'docker ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers -# 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 +ENV GOSU_VERSION=1.10 \ + GOMPLATE_VERSION=2.4.0 RUN set -xe; \ - curl -sSL "https://github.com/tianon/gosu/releases/download/1.10/gosu-$(dpkg --print-architecture)" -o /usr/local/bin/gosu; \ + # 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 + 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/ @@ -117,7 +121,7 @@ RUN set -xe; \ echo "export VISIBLE=now" >> /etc/profile ENV NOTVISIBLE "in users profile" -# Install PHP extentions +# PHP RUN set -xe; \ buildDeps=" \ g++ \ @@ -143,7 +147,7 @@ RUN set -xe; \ apt-get update >/dev/null; \ # Necessary for msodbcsql17 (MSSQL) ACCEPT_EULA=Y \ - apt-get -y --force-yes --no-install-recommends install >/dev/null \ + apt-get -y --no-install-recommends install >/dev/null \ $buildDeps \ blackfire-php \ libc-client2007e \ @@ -223,102 +227,100 @@ RUN set -xe; \ apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $buildDeps >/dev/null; \ apt-get clean; rm -rf /var/lib/apt/lists/* -# Copy mhsendmail binary from stage 1 -COPY --from=mhbuild /go/bin/mhsendmail /usr/local/bin/mhsendmail - +# PHP tools (installed globally) ENV COMPOSER_VERSION=1.6.3 \ DRUSH_VERSION=8.1.16 \ DRUSH_LAUNCHER_VERSION=0.6.0 \ - DRUPAL_CONSOLE_VERSION=1.7.0 \ + DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8 \ + DRUPAL_CONSOLE_LAUNCHER_VERSION=1.7.0 \ WPCLI_VERSION=1.5.0 \ - MG_CODEGEN_VERSION=1.10 \ BLACKFIRE_VERSION=1.15.0 \ - GOMPLATE_VERSION=2.4.0 + PLATFORMSH_CLI_VERSION=3.33.5 RUN set -xe; \ # Composer - curl -sSL "https://github.com/composer/composer/releases/download/${COMPOSER_VERSION}/composer.phar" -o /usr/local/bin/composer; \ - # Drush 8 (default) - curl -sSL "https://github.com/drush-ops/drush/releases/download/${DRUSH_VERSION}/drush.phar" -o /usr/local/bin/drush8; \ + 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 -sSL "https://github.com/drush-ops/drush-launcher/releases/download/${DRUSH_LAUNCHER_VERSION}/drush.phar" -o /usr/local/bin/drush; \ - # Drupal Console - curl -sSL "https://github.com/hechoendrupal/drupal-console-launcher/releases/download/${DRUPAL_CONSOLE_VERSION}/drupal.phar" -o /usr/local/bin/drupal; \ + 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 -sSL "https://github.com/wp-cli/wp-cli/releases/download/v${WPCLI_VERSION}/wp-cli-${WPCLI_VERSION}.phar" -o /usr/local/bin/wp; \ - # Magento2 Code Generator - curl -sSL "https://github.com/staempfli/magento2-code-generator/releases/download/${MG_CODEGEN_VERSION}/mg2-codegen.phar" -o /usr/local/bin/mg2-codegen; \ + 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 -sSL https://packages.blackfire.io/binaries/blackfire-agent/${BLACKFIRE_VERSION}/blackfire-cli-linux_static_amd64 -o /usr/local/bin/blackfire; \ - # gomplate - curl -sSL https://github.com/hairyhenderson/gomplate/releases/download/v${GOMPLATE_VERSION}/gomplate_linux-amd64-slim -o /usr/local/bin/gomplate; \ - # Make all binaries executable in one shot - chmod +x /usr/local/bin/* + 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); -# Other language packages and dependencies +# Ruby RUN set -xe; \ - apt-get update >/dev/null; apt-get -y --force-yes --no-install-recommends install >/dev/null \ + apt-get update >/dev/null; \ + apt-get -y --no-install-recommends install >/dev/null \ ruby-full \ - rlwrap; \ - #build-essential; \ + rlwrap \ + ;\ # Cleanup - apt-get clean; rm -rf /var/lib/apt/lists/* - -# bundler -RUN gem install bundler >/dev/null + apt-get clean; rm -rf /var/lib/apt/lists/*; \ + # bundler + gem install bundler >/dev/null # Home directory for bundle installs ENV BUNDLE_PATH .bundler # All further RUN commands will run as the "docker" user USER docker -ENV HOME /home/docker +ARG HOME=/home/docker -# Install nvm and a default node version -ENV NVM_VERSION=0.33.8 \ - NODE_VERSION=8.11.0 \ - NVM_DIR=$HOME/.nvm -# Don't use -x here - node/nvm stuff prints just too much stuff -RUN set -e; \ - curl -sSL https://raw.githubusercontent.com/creationix/nvm/v${NVM_VERSION}/install.sh | bash; \ - . $NVM_DIR/nvm.sh; \ - nvm install $NODE_VERSION >/dev/null; \ - nvm alias default $NODE_VERSION; \ - # Install global node packages - npm install -g npm >/dev/null; \ - # Cleanup - nvm clear-cache && npm cache clear --force; \ - # Fix npm complaining about permissions and not being able to update - sudo rm -rf $HOME/.config - -# Install Composer based dependencies +# PHP tools (installed as user) ENV PATH=$PATH:$HOME/.composer/vendor/bin \ - DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8 -ENV PATH=$PATH:$HOME/.terminus/vendor/bin \ - TERMINUS_VERSION=1.8.0 + MG_CODEGEN_VERSION=1.10 \ + TERMINUS_VERSION=1.8.1 RUN set -xe; \ + \ + # Composer based dependencies # Add composer bin directory to PATH echo "\n"'PATH="$PATH:$HOME/.composer/vendor/bin"' >> $HOME/.profile; \ - # Drush modules - drush dl registry_rebuild --default-major=7 --destination=$HOME/.drush >/dev/null; \ - drush cc drush; \ - # Drupal Coder w/ a matching version of PHP_CodeSniffer - composer global require drupal/coder >/dev/null; \ - phpcs --config-set installed_paths $HOME/.composer/vendor/drupal/coder/coder_sniffer; \ + # 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/vendor/drupal/coder/coder_sniffer; \ + # Magento2 Code Generator + cgr staempfli/magento2-code-generator:${MG_CODEGEN_VERSION} >/dev/null; \ # Terminus - # Installed in a dedicated directory to avoid dependency conflicts. - echo "\n"'PATH="$PATH:$HOME/.terminus/vendor/bin"' >> $HOME/.profile; \ - mkdir -p $HOME/.terminus; \ - # Run in a subshell since we are doing directory switching - (cd $HOME/.terminus && composer require pantheon-systems/terminus:${TERMINUS_VERSION}); \ + cgr pantheon-systems/terminus:${TERMINUS_VERSION} >/dev/null; \ # Cleanup - composer clear-cache + composer clear-cache; \ + \ + # Drush modules + drush dl registry_rebuild --default-major=7 --destination=$HOME/.drush >/dev/null; \ + 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 +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 USER root +# 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/.acquia $HOME/.acquia -COPY --chown=docker:docker config/.docksalrc $HOME/.docksalrc COPY --chown=docker:docker config/.drush $HOME/.drush COPY --chown=docker:docker config/.ssh $HOME/.ssh COPY config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf diff --git a/7.0/Makefile b/7.0/Makefile index 0f075d49..792cebd2 100644 --- a/7.0/Makefile +++ b/7.0/Makefile @@ -1,10 +1,11 @@ +-include ../tests/env_make -include env_make -PHP_VERSION = 7.0 -VERSION ?= php-fpm-$(PHP_VERSION) - +VERSION ?= 7.0 REPO = docksal/cli -NAME = docksal-cli-$(PHP_VERSION) +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 @@ -12,25 +13,30 @@ VOLUMES += -v /home/docker .PHONY: build test push shell run start stop logs clean release build: - docker build -t $(REPO):$(VERSION) . + docker build -t $(REPO):$(TAG) . test: - IMAGE=$(REPO):$(VERSION) NAME=$(NAME) PHP_VERSION=$(PHP_VERSION) ../tests/test.bats + IMAGE=$(REPO):$(TAG) NAME=$(NAME) VERSION=$(VERSION) ../tests/test.bats push: - docker push $(REPO):$(VERSION) - -shell: - docker run --rm --name $(NAME) -it $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(VERSION) /bin/bash + docker push $(REPO):$(TAG) -run: - docker run --rm --name $(NAME) -it $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(VERSION) +run: clean + docker run --rm --name $(NAME) -it $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(TAG) -start: - docker run -d --name $(NAME) $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(VERSION) +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 -it $(NAME) /bin/bash + @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) @@ -38,10 +44,13 @@ stop: logs: docker logs $(NAME) +logs-follow: + docker logs -f $(NAME) + clean: - docker rm -f $(NAME) + docker rm -vf $(NAME) >/dev/null 2>&1 || true release: build - make push -e VERSION=$(VERSION) + make push -e TAG=$(TAG) default: build diff --git a/7.0/config/.docksalrc b/7.0/config/.docksalrc deleted file mode 100644 index 576167b7..00000000 --- a/7.0/config/.docksalrc +++ /dev/null @@ -1,13 +0,0 @@ -# Commands in this file will be sourced for both interactive and non-interactive sessions. - -# Allow alias expansion in non-interactive shells. -shopt -s expand_aliases - -# NVM initialization. -export NVM_DIR="/home/docker/.nvm" -[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm - -# Source alias definitions. -if [ -f ~/.bash_aliases ]; then - . ~/.bash_aliases -fi diff --git a/7.0/config/php/zz-php.ini b/7.0/config/php/zz-php.ini index 2da599b5..0b1aff21 100644 --- a/7.0/config/php/zz-php.ini +++ b/7.0/config/php/zz-php.ini @@ -1,5 +1,5 @@ ; PHP global (CLI and FPM) settings -; To override settings for FPM use docksal-www.conf +; To override settings for FPM use zz-php-fpm.conf [php] memory_limit = 1024M max_execution_time = 600 diff --git a/7.0/config/supervisord.conf b/7.0/config/supervisord.conf index 32b078b2..d7306e21 100644 --- a/7.0/config/supervisord.conf +++ b/7.0/config/supervisord.conf @@ -1,37 +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 -# ---------------------------------------------------------------------------------------------------- -# Optional stuff to make supervisord complain less about misc things not being configured -logfile = /var/log/supervisor/supervisord.log -pidfile = /var/run/supervisord.pid - -[unix_http_server] -file = /var/run/supervisord.sock -chmod = 0700 -username = dummy -password = dummy - -[supervisorctl] -serverurl = unix:///var/run/supervisord.sock -username = dummy -password = dummy - -[rpcinterface:supervisor] -supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface -# END: Optional stuff to make supervisord complain less about misc things not being configured -# ---------------------------------------------------------------------------------------------------- [program:php-fpm] command = /usr/local/sbin/php-fpm -stdout_logfile = /dev/stdout -stdout_logfile_maxbytes = 0 -stderr_logfile = /dev/stderr -stderr_logfile_maxbytes = 0 +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 = /dev/stdout -stdout_logfile_maxbytes = 0 -stderr_logfile = /dev/stderr -stderr_logfile_maxbytes = 0 +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.0/startup.sh b/7.0/startup.sh index 9a577e6b..7a615cae 100755 --- a/7.0/startup.sh +++ b/7.0/startup.sh @@ -43,10 +43,40 @@ render_tmpl () 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 +} + +# Pantheon authentication terminus_login () { echo-debug "Authenticating with Pantheon..." - terminus auth:login --machine-token="$SECRET_TERMINUS_TOKEN" >/dev/null 2>&1 + # This has to be done using the docker user via su to load the user environment + # Note: 'su -' = 'su -l' = 'su --login' + local output + output=$(sudo su - docker -c "terminus auth:login --machine-token='${TERMINUS_TOKEN}'" 2>&1) + #>/dev/null 2>&1 + if [[ $? != 0 ]]; then + echo-debug "ERROR: Pantheon authentication failed." + echo + echo "$output" + echo + fi } # Process templates @@ -56,8 +86,8 @@ chmod 0600 "$HOME_DIR/.ssh/id_rsa" # Acquia Cloud API config render_tmpl "$HOME_DIR/.acquia/cloudapi.conf" -# Terminus authentication -[[ "$SECRET_TERMINUS_TOKEN" ]] && terminus_login +# 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 @@ -68,25 +98,44 @@ render_tmpl "$HOME_DIR/.acquia/cloudapi.conf" # 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:$HOST_GID" -R "$HOME_DIR" +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. -chown "$HOST_UID:$HOST_GID" /var/www +# We apply a fix/workaround for this at startup (non-recursive). +chown "${HOST_UID-:1000}:${HOST_GID:-1000}" /var/www + +# Automatically authenticate with Pantheon in Terminus token is present +# Note: this has to happen after th home directory permissions are reset, +# otherwise the docker user may not have write access to /home/.terminus, where the auth session data is stored. +[[ "$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 # Initialization steps completed. Create a pid file to mark the container as healthy -echo-debug "Preliminary initialization completed" +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? + ${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 "Executing the requested command..." +echo-debug "Passing execution to: $*" # Service mode (run as root) if [[ "$1" == "supervisord" ]]; then - exec gosu root supervisord -c /etc/supervisor/conf.d/supervisord.conf + exec gosu root supervisord -c /etc/supervisor/supervisord.conf # Command mode (run as docker user) else - # This makes sure the environment is set up correctly for the docker user - DOCKSALRC='source $HOME/.docksalrc >/dev/null 2>&1' - # Launch the passed command in an non-interactive bash session under docker user - # $@ does not work here. $* has to be used. - exec gosu docker bash -c "$DOCKSALRC; exec $*" + exec gosu docker "$@" fi diff --git a/7.1/Dockerfile b/7.1/Dockerfile index e1d3dbcb..2d466f8b 100644 --- a/7.1/Dockerfile +++ b/7.1/Dockerfile @@ -11,6 +11,7 @@ RUN set -xe; \ FROM php:7.1-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; \ @@ -18,7 +19,8 @@ RUN set -xe; \ # Install basic packages RUN set -xe; \ - apt-get update >/dev/null; apt-get -y --force-yes --no-install-recommends install >/dev/null \ + apt-get update >/dev/null; \ + apt-get -y --no-install-recommends install >/dev/null \ apt-transport-https \ ca-certificates \ curl \ @@ -42,17 +44,14 @@ RUN set -xe; \ # backports repo echo "deb http://ftp.debian.org/debian jessie-backports main" | tee /etc/apt/sources.list.d/backports.list; \ # blackfire.io repo - curl -sSL https://packagecloud.io/gpg.key | apt-key add -; \ + curl -fsSL https://packagecloud.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 -sSL https://packagecloud.io/github/git-lfs/gpgkey | apt-key add -; \ + curl -fsSL https://packagecloud.io/github/git-lfs/gpgkey | apt-key add -; \ echo 'deb https://packagecloud.io/github/git-lfs/debian/ jessie main' | tee /etc/apt/sources.list.d/github_git-lfs.list; \ echo 'deb-src https://packagecloud.io/github/git-lfs/debian/ jessie main' | tee -a /etc/apt/sources.list.d/github_git-lfs.list; \ - # yarn repo - curl -sSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -; \ - echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list; \ # MSQSQL repo - msodbcsql17, pecl/sqlsrv and pecl/pdo_sqlsrv (PHP 7.0+ only) - curl -sSL https://packages.microsoft.com/keys/microsoft.asc | apt-key add -; \ + curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | apt-key add -; \ echo 'deb https://packages.microsoft.com/debian/8/prod jessie main' | tee /etc/apt/sources.list.d/mssql.list; # Additional packages @@ -60,7 +59,8 @@ 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 --force-yes --no-install-recommends install >/dev/null \ + apt-get update >/dev/null; \ + apt-get -y --no-install-recommends install >/dev/null \ cron \ dnsutils \ git-lfs \ @@ -85,10 +85,9 @@ RUN set -xe; \ unzip \ zip \ zsh \ - yarn \ ;\ # More recent version of git to get composer's git cache. - apt-get -y --force-yes --no-install-recommends -t jessie-backports install git >/dev/null ;\ + apt-get -y --no-install-recommends -t jessie-backports install git >/dev/null ;\ # Cleanup apt-get clean; rm -rf /var/lib/apt/lists/* @@ -98,13 +97,18 @@ RUN set -xe; \ useradd -m -s /bin/bash -u 1000 -g 1000 -G sudo -p docker docker; \ echo 'docker ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers -# 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 +ENV GOSU_VERSION=1.10 \ + GOMPLATE_VERSION=2.4.0 RUN set -xe; \ - curl -sSL "https://github.com/tianon/gosu/releases/download/1.10/gosu-$(dpkg --print-architecture)" -o /usr/local/bin/gosu; \ + # 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 + 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/ @@ -117,7 +121,7 @@ RUN set -xe; \ echo "export VISIBLE=now" >> /etc/profile ENV NOTVISIBLE "in users profile" -# Install PHP extentions +# PHP RUN set -xe; \ buildDeps=" \ g++ \ @@ -143,7 +147,7 @@ RUN set -xe; \ apt-get update >/dev/null; \ # Necessary for msodbcsql17 (MSSQL) ACCEPT_EULA=Y \ - apt-get -y --force-yes --no-install-recommends install >/dev/null \ + apt-get -y --no-install-recommends install >/dev/null \ $buildDeps \ blackfire-php \ libc-client2007e \ @@ -223,102 +227,100 @@ RUN set -xe; \ apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $buildDeps >/dev/null; \ apt-get clean; rm -rf /var/lib/apt/lists/* -# Copy mhsendmail binary from stage 1 -COPY --from=mhbuild /go/bin/mhsendmail /usr/local/bin/mhsendmail - +# PHP tools (installed globally) ENV COMPOSER_VERSION=1.6.3 \ DRUSH_VERSION=8.1.16 \ DRUSH_LAUNCHER_VERSION=0.6.0 \ - DRUPAL_CONSOLE_VERSION=1.7.0 \ + DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8 \ + DRUPAL_CONSOLE_LAUNCHER_VERSION=1.7.0 \ WPCLI_VERSION=1.5.0 \ - MG_CODEGEN_VERSION=1.10 \ BLACKFIRE_VERSION=1.15.0 \ - GOMPLATE_VERSION=2.4.0 + PLATFORMSH_CLI_VERSION=3.33.5 RUN set -xe; \ # Composer - curl -sSL "https://github.com/composer/composer/releases/download/${COMPOSER_VERSION}/composer.phar" -o /usr/local/bin/composer; \ - # Drush 8 (default) - curl -sSL "https://github.com/drush-ops/drush/releases/download/${DRUSH_VERSION}/drush.phar" -o /usr/local/bin/drush8; \ + 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 -sSL "https://github.com/drush-ops/drush-launcher/releases/download/${DRUSH_LAUNCHER_VERSION}/drush.phar" -o /usr/local/bin/drush; \ - # Drupal Console - curl -sSL "https://github.com/hechoendrupal/drupal-console-launcher/releases/download/${DRUPAL_CONSOLE_VERSION}/drupal.phar" -o /usr/local/bin/drupal; \ + 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 -sSL "https://github.com/wp-cli/wp-cli/releases/download/v${WPCLI_VERSION}/wp-cli-${WPCLI_VERSION}.phar" -o /usr/local/bin/wp; \ - # Magento2 Code Generator - curl -sSL "https://github.com/staempfli/magento2-code-generator/releases/download/${MG_CODEGEN_VERSION}/mg2-codegen.phar" -o /usr/local/bin/mg2-codegen; \ + 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 -sSL https://packages.blackfire.io/binaries/blackfire-agent/${BLACKFIRE_VERSION}/blackfire-cli-linux_static_amd64 -o /usr/local/bin/blackfire; \ - # gomplate - curl -sSL https://github.com/hairyhenderson/gomplate/releases/download/v${GOMPLATE_VERSION}/gomplate_linux-amd64-slim -o /usr/local/bin/gomplate; \ - # Make all binaries executable in one shot - chmod +x /usr/local/bin/* + 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); -# Other language packages and dependencies +# Ruby RUN set -xe; \ - apt-get update >/dev/null; apt-get -y --force-yes --no-install-recommends install >/dev/null \ + apt-get update >/dev/null; \ + apt-get -y --no-install-recommends install >/dev/null \ ruby-full \ - rlwrap; \ - #build-essential; \ + rlwrap \ + ;\ # Cleanup - apt-get clean; rm -rf /var/lib/apt/lists/* - -# bundler -RUN gem install bundler >/dev/null + apt-get clean; rm -rf /var/lib/apt/lists/*; \ + # bundler + gem install bundler >/dev/null # Home directory for bundle installs ENV BUNDLE_PATH .bundler # All further RUN commands will run as the "docker" user USER docker -ENV HOME /home/docker +ARG HOME=/home/docker -# Install nvm and a default node version -ENV NVM_VERSION=0.33.8 \ - NODE_VERSION=8.11.0 \ - NVM_DIR=$HOME/.nvm -# Don't use -x here - node/nvm stuff prints just too much stuff -RUN set -e; \ - curl -sSL https://raw.githubusercontent.com/creationix/nvm/v${NVM_VERSION}/install.sh | bash; \ - . $NVM_DIR/nvm.sh; \ - nvm install $NODE_VERSION >/dev/null; \ - nvm alias default $NODE_VERSION; \ - # Install global node packages - npm install -g npm >/dev/null; \ - # Cleanup - nvm clear-cache && npm cache clear --force; \ - # Fix npm complaining about permissions and not being able to update - sudo rm -rf $HOME/.config - -# Install Composer based dependencies +# PHP tools (installed as user) ENV PATH=$PATH:$HOME/.composer/vendor/bin \ - DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8 -ENV PATH=$PATH:$HOME/.terminus/vendor/bin \ - TERMINUS_VERSION=1.8.0 + MG_CODEGEN_VERSION=1.10 \ + TERMINUS_VERSION=1.8.1 RUN set -xe; \ + \ + # Composer based dependencies # Add composer bin directory to PATH echo "\n"'PATH="$PATH:$HOME/.composer/vendor/bin"' >> $HOME/.profile; \ - # Drush modules - drush dl registry_rebuild --default-major=7 --destination=$HOME/.drush >/dev/null; \ - drush cc drush; \ - # Drupal Coder w/ a matching version of PHP_CodeSniffer - composer global require drupal/coder >/dev/null; \ - phpcs --config-set installed_paths $HOME/.composer/vendor/drupal/coder/coder_sniffer; \ + # 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/vendor/drupal/coder/coder_sniffer; \ + # Magento2 Code Generator + cgr staempfli/magento2-code-generator:${MG_CODEGEN_VERSION} >/dev/null; \ # Terminus - # Installed in a dedicated directory to avoid dependency conflicts. - echo "\n"'PATH="$PATH:$HOME/.terminus/vendor/bin"' >> $HOME/.profile; \ - mkdir -p $HOME/.terminus; \ - # Run in a subshell since we are doing directory switching - (cd $HOME/.terminus && composer require pantheon-systems/terminus:${TERMINUS_VERSION}); \ + cgr pantheon-systems/terminus:${TERMINUS_VERSION} >/dev/null; \ # Cleanup - composer clear-cache + composer clear-cache; \ + \ + # Drush modules + drush dl registry_rebuild --default-major=7 --destination=$HOME/.drush >/dev/null; \ + 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 +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 USER root +# 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/.acquia $HOME/.acquia -COPY --chown=docker:docker config/.docksalrc $HOME/.docksalrc COPY --chown=docker:docker config/.drush $HOME/.drush COPY --chown=docker:docker config/.ssh $HOME/.ssh COPY config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf diff --git a/7.1/Makefile b/7.1/Makefile index 0952ff90..7441f0fe 100644 --- a/7.1/Makefile +++ b/7.1/Makefile @@ -1,10 +1,11 @@ +-include ../tests/env_make -include env_make -PHP_VERSION = 7.1 -VERSION ?= php-fpm-$(PHP_VERSION) - +VERSION ?= 7.1 REPO = docksal/cli -NAME = docksal-cli-$(PHP_VERSION) +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 @@ -12,25 +13,30 @@ VOLUMES += -v /home/docker .PHONY: build test push shell run start stop logs clean release build: - docker build -t $(REPO):$(VERSION) . + docker build -t $(REPO):$(TAG) . test: - IMAGE=$(REPO):$(VERSION) NAME=$(NAME) PHP_VERSION=$(PHP_VERSION) ../tests/test.bats + IMAGE=$(REPO):$(TAG) NAME=$(NAME) VERSION=$(VERSION) ../tests/test.bats push: - docker push $(REPO):$(VERSION) - -shell: - docker run --rm --name $(NAME) -it $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(VERSION) /bin/bash + docker push $(REPO):$(TAG) -run: - docker run --rm --name $(NAME) -it $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(VERSION) +run: clean + docker run --rm --name $(NAME) -it $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(TAG) -start: - docker run -d --name $(NAME) $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(VERSION) +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 -it $(NAME) /bin/bash + @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) @@ -38,10 +44,13 @@ stop: logs: docker logs $(NAME) +logs-follow: + docker logs -f $(NAME) + clean: - docker rm -f $(NAME) + docker rm -vf $(NAME) >/dev/null 2>&1 || true release: build - make push -e VERSION=$(VERSION) + make push -e TAG=$(TAG) default: build diff --git a/7.1/config/.docksalrc b/7.1/config/.docksalrc deleted file mode 100644 index 576167b7..00000000 --- a/7.1/config/.docksalrc +++ /dev/null @@ -1,13 +0,0 @@ -# Commands in this file will be sourced for both interactive and non-interactive sessions. - -# Allow alias expansion in non-interactive shells. -shopt -s expand_aliases - -# NVM initialization. -export NVM_DIR="/home/docker/.nvm" -[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm - -# Source alias definitions. -if [ -f ~/.bash_aliases ]; then - . ~/.bash_aliases -fi diff --git a/7.1/config/php/zz-php.ini b/7.1/config/php/zz-php.ini index 2da599b5..0b1aff21 100644 --- a/7.1/config/php/zz-php.ini +++ b/7.1/config/php/zz-php.ini @@ -1,5 +1,5 @@ ; PHP global (CLI and FPM) settings -; To override settings for FPM use docksal-www.conf +; To override settings for FPM use zz-php-fpm.conf [php] memory_limit = 1024M max_execution_time = 600 diff --git a/7.1/config/supervisord.conf b/7.1/config/supervisord.conf index 32b078b2..d7306e21 100644 --- a/7.1/config/supervisord.conf +++ b/7.1/config/supervisord.conf @@ -1,37 +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 -# ---------------------------------------------------------------------------------------------------- -# Optional stuff to make supervisord complain less about misc things not being configured -logfile = /var/log/supervisor/supervisord.log -pidfile = /var/run/supervisord.pid - -[unix_http_server] -file = /var/run/supervisord.sock -chmod = 0700 -username = dummy -password = dummy - -[supervisorctl] -serverurl = unix:///var/run/supervisord.sock -username = dummy -password = dummy - -[rpcinterface:supervisor] -supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface -# END: Optional stuff to make supervisord complain less about misc things not being configured -# ---------------------------------------------------------------------------------------------------- [program:php-fpm] command = /usr/local/sbin/php-fpm -stdout_logfile = /dev/stdout -stdout_logfile_maxbytes = 0 -stderr_logfile = /dev/stderr -stderr_logfile_maxbytes = 0 +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 = /dev/stdout -stdout_logfile_maxbytes = 0 -stderr_logfile = /dev/stderr -stderr_logfile_maxbytes = 0 +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.1/startup.sh b/7.1/startup.sh index 9a577e6b..7a615cae 100755 --- a/7.1/startup.sh +++ b/7.1/startup.sh @@ -43,10 +43,40 @@ render_tmpl () 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 +} + +# Pantheon authentication terminus_login () { echo-debug "Authenticating with Pantheon..." - terminus auth:login --machine-token="$SECRET_TERMINUS_TOKEN" >/dev/null 2>&1 + # This has to be done using the docker user via su to load the user environment + # Note: 'su -' = 'su -l' = 'su --login' + local output + output=$(sudo su - docker -c "terminus auth:login --machine-token='${TERMINUS_TOKEN}'" 2>&1) + #>/dev/null 2>&1 + if [[ $? != 0 ]]; then + echo-debug "ERROR: Pantheon authentication failed." + echo + echo "$output" + echo + fi } # Process templates @@ -56,8 +86,8 @@ chmod 0600 "$HOME_DIR/.ssh/id_rsa" # Acquia Cloud API config render_tmpl "$HOME_DIR/.acquia/cloudapi.conf" -# Terminus authentication -[[ "$SECRET_TERMINUS_TOKEN" ]] && terminus_login +# 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 @@ -68,25 +98,44 @@ render_tmpl "$HOME_DIR/.acquia/cloudapi.conf" # 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:$HOST_GID" -R "$HOME_DIR" +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. -chown "$HOST_UID:$HOST_GID" /var/www +# We apply a fix/workaround for this at startup (non-recursive). +chown "${HOST_UID-:1000}:${HOST_GID:-1000}" /var/www + +# Automatically authenticate with Pantheon in Terminus token is present +# Note: this has to happen after th home directory permissions are reset, +# otherwise the docker user may not have write access to /home/.terminus, where the auth session data is stored. +[[ "$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 # Initialization steps completed. Create a pid file to mark the container as healthy -echo-debug "Preliminary initialization completed" +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? + ${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 "Executing the requested command..." +echo-debug "Passing execution to: $*" # Service mode (run as root) if [[ "$1" == "supervisord" ]]; then - exec gosu root supervisord -c /etc/supervisor/conf.d/supervisord.conf + exec gosu root supervisord -c /etc/supervisor/supervisord.conf # Command mode (run as docker user) else - # This makes sure the environment is set up correctly for the docker user - DOCKSALRC='source $HOME/.docksalrc >/dev/null 2>&1' - # Launch the passed command in an non-interactive bash session under docker user - # $@ does not work here. $* has to be used. - exec gosu docker bash -c "$DOCKSALRC; exec $*" + exec gosu docker "$@" fi diff --git a/7.2/Dockerfile b/7.2/Dockerfile index 8f3968ed..063afd8d 100644 --- a/7.2/Dockerfile +++ b/7.2/Dockerfile @@ -11,6 +11,7 @@ RUN set -xe; \ FROM php:7.2-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; \ @@ -18,7 +19,8 @@ RUN set -xe; \ # Install basic packages RUN set -xe; \ - apt-get update >/dev/null; apt-get -y --force-yes --no-install-recommends install >/dev/null \ + apt-get update >/dev/null; \ + apt-get -y --no-install-recommends install >/dev/null \ apt-transport-https \ ca-certificates \ curl \ @@ -42,17 +44,14 @@ RUN set -xe; \ # backports repo echo "deb http://ftp.debian.org/debian jessie-backports main" | tee /etc/apt/sources.list.d/backports.list; \ # blackfire.io repo - curl -sSL https://packagecloud.io/gpg.key | apt-key add -; \ + curl -fsSL https://packagecloud.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 -sSL https://packagecloud.io/github/git-lfs/gpgkey | apt-key add -; \ + curl -fsSL https://packagecloud.io/github/git-lfs/gpgkey | apt-key add -; \ echo 'deb https://packagecloud.io/github/git-lfs/debian/ jessie main' | tee /etc/apt/sources.list.d/github_git-lfs.list; \ echo 'deb-src https://packagecloud.io/github/git-lfs/debian/ jessie main' | tee -a /etc/apt/sources.list.d/github_git-lfs.list; \ - # yarn repo - curl -sSL https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add -; \ - echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list; \ # MSQSQL repo - msodbcsql17, pecl/sqlsrv and pecl/pdo_sqlsrv (PHP 7.0+ only) - curl -sSL https://packages.microsoft.com/keys/microsoft.asc | apt-key add -; \ + curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | apt-key add -; \ echo 'deb https://packages.microsoft.com/debian/8/prod jessie main' | tee /etc/apt/sources.list.d/mssql.list; # Additional packages @@ -60,7 +59,8 @@ 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 --force-yes --no-install-recommends install >/dev/null \ + apt-get update >/dev/null; \ + apt-get -y --no-install-recommends install >/dev/null \ cron \ dnsutils \ git-lfs \ @@ -85,10 +85,9 @@ RUN set -xe; \ unzip \ zip \ zsh \ - yarn \ ;\ # More recent version of git to get composer's git cache. - apt-get -y --force-yes --no-install-recommends -t jessie-backports install git >/dev/null ;\ + apt-get -y --no-install-recommends -t jessie-backports install git >/dev/null ;\ # Cleanup apt-get clean; rm -rf /var/lib/apt/lists/* @@ -98,13 +97,18 @@ RUN set -xe; \ useradd -m -s /bin/bash -u 1000 -g 1000 -G sudo -p docker docker; \ echo 'docker ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers -# 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 +ENV GOSU_VERSION=1.10 \ + GOMPLATE_VERSION=2.4.0 RUN set -xe; \ - curl -sSL "https://github.com/tianon/gosu/releases/download/1.10/gosu-$(dpkg --print-architecture)" -o /usr/local/bin/gosu; \ + # 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 + 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/ @@ -117,7 +121,7 @@ RUN set -xe; \ echo "export VISIBLE=now" >> /etc/profile ENV NOTVISIBLE "in users profile" -# Install PHP extentions +# PHP RUN set -xe; \ buildDeps=" \ g++ \ @@ -143,7 +147,7 @@ RUN set -xe; \ apt-get update >/dev/null; \ # Necessary for msodbcsql17 (MSSQL) ACCEPT_EULA=Y \ - apt-get -y --force-yes --no-install-recommends install >/dev/null \ + apt-get -y --no-install-recommends install >/dev/null \ $buildDeps \ blackfire-php \ libc-client2007e \ @@ -225,102 +229,100 @@ RUN set -xe; \ apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $buildDeps >/dev/null; \ apt-get clean; rm -rf /var/lib/apt/lists/* -# Copy mhsendmail binary from stage 1 -COPY --from=mhbuild /go/bin/mhsendmail /usr/local/bin/mhsendmail - +# PHP tools (installed globally) ENV COMPOSER_VERSION=1.6.3 \ DRUSH_VERSION=8.1.16 \ DRUSH_LAUNCHER_VERSION=0.6.0 \ - DRUPAL_CONSOLE_VERSION=1.7.0 \ + DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8 \ + DRUPAL_CONSOLE_LAUNCHER_VERSION=1.7.0 \ WPCLI_VERSION=1.5.0 \ - MG_CODEGEN_VERSION=1.10 \ BLACKFIRE_VERSION=1.15.0 \ - GOMPLATE_VERSION=2.4.0 + PLATFORMSH_CLI_VERSION=3.33.5 RUN set -xe; \ # Composer - curl -sSL "https://github.com/composer/composer/releases/download/${COMPOSER_VERSION}/composer.phar" -o /usr/local/bin/composer; \ - # Drush 8 (default) - curl -sSL "https://github.com/drush-ops/drush/releases/download/${DRUSH_VERSION}/drush.phar" -o /usr/local/bin/drush8; \ + 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 -sSL "https://github.com/drush-ops/drush-launcher/releases/download/${DRUSH_LAUNCHER_VERSION}/drush.phar" -o /usr/local/bin/drush; \ - # Drupal Console - curl -sSL "https://github.com/hechoendrupal/drupal-console-launcher/releases/download/${DRUPAL_CONSOLE_VERSION}/drupal.phar" -o /usr/local/bin/drupal; \ + 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 -sSL "https://github.com/wp-cli/wp-cli/releases/download/v${WPCLI_VERSION}/wp-cli-${WPCLI_VERSION}.phar" -o /usr/local/bin/wp; \ - # Magento2 Code Generator - curl -sSL "https://github.com/staempfli/magento2-code-generator/releases/download/${MG_CODEGEN_VERSION}/mg2-codegen.phar" -o /usr/local/bin/mg2-codegen; \ + 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 -sSL https://packages.blackfire.io/binaries/blackfire-agent/${BLACKFIRE_VERSION}/blackfire-cli-linux_static_amd64 -o /usr/local/bin/blackfire; \ - # gomplate - curl -sSL https://github.com/hairyhenderson/gomplate/releases/download/v${GOMPLATE_VERSION}/gomplate_linux-amd64-slim -o /usr/local/bin/gomplate; \ - # Make all binaries executable in one shot - chmod +x /usr/local/bin/* + 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); -# Other language packages and dependencies +# Ruby RUN set -xe; \ - apt-get update >/dev/null; apt-get -y --force-yes --no-install-recommends install >/dev/null \ + apt-get update >/dev/null; \ + apt-get -y --no-install-recommends install >/dev/null \ ruby-full \ - rlwrap; \ - #build-essential; \ + rlwrap \ + ;\ # Cleanup - apt-get clean; rm -rf /var/lib/apt/lists/* - -# bundler -RUN gem install bundler >/dev/null + apt-get clean; rm -rf /var/lib/apt/lists/*; \ + # bundler + gem install bundler >/dev/null # Home directory for bundle installs ENV BUNDLE_PATH .bundler # All further RUN commands will run as the "docker" user USER docker -ENV HOME /home/docker +ARG HOME=/home/docker -# Install nvm and a default node version -ENV NVM_VERSION=0.33.8 \ - NODE_VERSION=8.11.0 \ - NVM_DIR=$HOME/.nvm -# Don't use -x here - node/nvm stuff prints just too much stuff -RUN set -e; \ - curl -sSL https://raw.githubusercontent.com/creationix/nvm/v${NVM_VERSION}/install.sh | bash; \ - . $NVM_DIR/nvm.sh; \ - nvm install $NODE_VERSION >/dev/null; \ - nvm alias default $NODE_VERSION; \ - # Install global node packages - npm install -g npm >/dev/null; \ - # Cleanup - nvm clear-cache && npm cache clear --force; \ - # Fix npm complaining about permissions and not being able to update - sudo rm -rf $HOME/.config - -# Install Composer based dependencies +# PHP tools (installed as user) ENV PATH=$PATH:$HOME/.composer/vendor/bin \ - DRUSH_LAUNCHER_FALLBACK=/usr/local/bin/drush8 -ENV PATH=$PATH:$HOME/.terminus/vendor/bin \ - TERMINUS_VERSION=1.8.0 + MG_CODEGEN_VERSION=1.10 \ + TERMINUS_VERSION=1.8.1 RUN set -xe; \ + \ + # Composer based dependencies # Add composer bin directory to PATH echo "\n"'PATH="$PATH:$HOME/.composer/vendor/bin"' >> $HOME/.profile; \ - # Drush modules - drush dl registry_rebuild --default-major=7 --destination=$HOME/.drush >/dev/null; \ - drush cc drush; \ - # Drupal Coder w/ a matching version of PHP_CodeSniffer - composer global require drupal/coder >/dev/null; \ - phpcs --config-set installed_paths $HOME/.composer/vendor/drupal/coder/coder_sniffer; \ + # 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/vendor/drupal/coder/coder_sniffer; \ + # Magento2 Code Generator + cgr staempfli/magento2-code-generator:${MG_CODEGEN_VERSION} >/dev/null; \ # Terminus - # Installed in a dedicated directory to avoid dependency conflicts. - echo "\n"'PATH="$PATH:$HOME/.terminus/vendor/bin"' >> $HOME/.profile; \ - mkdir -p $HOME/.terminus; \ - # Run in a subshell since we are doing directory switching - (cd $HOME/.terminus && composer require pantheon-systems/terminus:${TERMINUS_VERSION}); \ + cgr pantheon-systems/terminus:${TERMINUS_VERSION} >/dev/null; \ # Cleanup - composer clear-cache + composer clear-cache; \ + \ + # Drush modules + drush dl registry_rebuild --default-major=7 --destination=$HOME/.drush >/dev/null; \ + 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 +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 USER root +# 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/.acquia $HOME/.acquia -COPY --chown=docker:docker config/.docksalrc $HOME/.docksalrc COPY --chown=docker:docker config/.drush $HOME/.drush COPY --chown=docker:docker config/.ssh $HOME/.ssh COPY config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf diff --git a/7.2/Makefile b/7.2/Makefile index 0720ee93..a138979b 100644 --- a/7.2/Makefile +++ b/7.2/Makefile @@ -1,10 +1,11 @@ +-include ../tests/env_make -include env_make -PHP_VERSION = 7.2 -VERSION ?= php-fpm-$(PHP_VERSION) - +VERSION ?= 7.2 REPO = docksal/cli -NAME = docksal-cli-$(PHP_VERSION) +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 @@ -12,25 +13,30 @@ VOLUMES += -v /home/docker .PHONY: build test push shell run start stop logs clean release build: - docker build -t $(REPO):$(VERSION) . + docker build -t $(REPO):$(TAG) . test: - IMAGE=$(REPO):$(VERSION) NAME=$(NAME) PHP_VERSION=$(PHP_VERSION) ../tests/test.bats + IMAGE=$(REPO):$(TAG) NAME=$(NAME) VERSION=$(VERSION) ../tests/test.bats push: - docker push $(REPO):$(VERSION) - -shell: - docker run --rm --name $(NAME) -it $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(VERSION) /bin/bash + docker push $(REPO):$(TAG) -run: - docker run --rm --name $(NAME) -it $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(VERSION) +run: clean + docker run --rm --name $(NAME) -it $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(TAG) -start: - docker run -d --name $(NAME) $(PORTS) $(VOLUMES) $(ENV) $(REPO):$(VERSION) +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 -it $(NAME) /bin/bash + @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) @@ -38,10 +44,13 @@ stop: logs: docker logs $(NAME) +logs-follow: + docker logs -f $(NAME) + clean: - docker rm -f $(NAME) + docker rm -vf $(NAME) >/dev/null 2>&1 || true release: build - make push -e VERSION=$(VERSION) + make push -e TAG=$(TAG) default: build diff --git a/7.2/config/.docksalrc b/7.2/config/.docksalrc deleted file mode 100644 index 576167b7..00000000 --- a/7.2/config/.docksalrc +++ /dev/null @@ -1,13 +0,0 @@ -# Commands in this file will be sourced for both interactive and non-interactive sessions. - -# Allow alias expansion in non-interactive shells. -shopt -s expand_aliases - -# NVM initialization. -export NVM_DIR="/home/docker/.nvm" -[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" # This loads nvm - -# Source alias definitions. -if [ -f ~/.bash_aliases ]; then - . ~/.bash_aliases -fi diff --git a/7.2/config/php/zz-php.ini b/7.2/config/php/zz-php.ini index 2da599b5..0b1aff21 100644 --- a/7.2/config/php/zz-php.ini +++ b/7.2/config/php/zz-php.ini @@ -1,5 +1,5 @@ ; PHP global (CLI and FPM) settings -; To override settings for FPM use docksal-www.conf +; To override settings for FPM use zz-php-fpm.conf [php] memory_limit = 1024M max_execution_time = 600 diff --git a/7.2/config/supervisord.conf b/7.2/config/supervisord.conf index 32b078b2..d7306e21 100644 --- a/7.2/config/supervisord.conf +++ b/7.2/config/supervisord.conf @@ -1,37 +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 -# ---------------------------------------------------------------------------------------------------- -# Optional stuff to make supervisord complain less about misc things not being configured -logfile = /var/log/supervisor/supervisord.log -pidfile = /var/run/supervisord.pid - -[unix_http_server] -file = /var/run/supervisord.sock -chmod = 0700 -username = dummy -password = dummy - -[supervisorctl] -serverurl = unix:///var/run/supervisord.sock -username = dummy -password = dummy - -[rpcinterface:supervisor] -supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface -# END: Optional stuff to make supervisord complain less about misc things not being configured -# ---------------------------------------------------------------------------------------------------- [program:php-fpm] command = /usr/local/sbin/php-fpm -stdout_logfile = /dev/stdout -stdout_logfile_maxbytes = 0 -stderr_logfile = /dev/stderr -stderr_logfile_maxbytes = 0 +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 = /dev/stdout -stdout_logfile_maxbytes = 0 -stderr_logfile = /dev/stderr -stderr_logfile_maxbytes = 0 +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.2/startup.sh b/7.2/startup.sh index 9a577e6b..7a615cae 100755 --- a/7.2/startup.sh +++ b/7.2/startup.sh @@ -43,10 +43,40 @@ render_tmpl () 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 +} + +# Pantheon authentication terminus_login () { echo-debug "Authenticating with Pantheon..." - terminus auth:login --machine-token="$SECRET_TERMINUS_TOKEN" >/dev/null 2>&1 + # This has to be done using the docker user via su to load the user environment + # Note: 'su -' = 'su -l' = 'su --login' + local output + output=$(sudo su - docker -c "terminus auth:login --machine-token='${TERMINUS_TOKEN}'" 2>&1) + #>/dev/null 2>&1 + if [[ $? != 0 ]]; then + echo-debug "ERROR: Pantheon authentication failed." + echo + echo "$output" + echo + fi } # Process templates @@ -56,8 +86,8 @@ chmod 0600 "$HOME_DIR/.ssh/id_rsa" # Acquia Cloud API config render_tmpl "$HOME_DIR/.acquia/cloudapi.conf" -# Terminus authentication -[[ "$SECRET_TERMINUS_TOKEN" ]] && terminus_login +# 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 @@ -68,25 +98,44 @@ render_tmpl "$HOME_DIR/.acquia/cloudapi.conf" # 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:$HOST_GID" -R "$HOME_DIR" +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. -chown "$HOST_UID:$HOST_GID" /var/www +# We apply a fix/workaround for this at startup (non-recursive). +chown "${HOST_UID-:1000}:${HOST_GID:-1000}" /var/www + +# Automatically authenticate with Pantheon in Terminus token is present +# Note: this has to happen after th home directory permissions are reset, +# otherwise the docker user may not have write access to /home/.terminus, where the auth session data is stored. +[[ "$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 # Initialization steps completed. Create a pid file to mark the container as healthy -echo-debug "Preliminary initialization completed" +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? + ${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 "Executing the requested command..." +echo-debug "Passing execution to: $*" # Service mode (run as root) if [[ "$1" == "supervisord" ]]; then - exec gosu root supervisord -c /etc/supervisor/conf.d/supervisord.conf + exec gosu root supervisord -c /etc/supervisor/supervisord.conf # Command mode (run as docker user) else - # This makes sure the environment is set up correctly for the docker user - DOCKSALRC='source $HOME/.docksalrc >/dev/null 2>&1' - # Launch the passed command in an non-interactive bash session under docker user - # $@ does not work here. $* has to be used. - exec gosu docker bash -c "$DOCKSALRC; exec $*" + exec gosu docker "$@" fi diff --git a/README.md b/README.md index 63ab47e7..64c18bb6 100644 --- a/README.md +++ b/README.md @@ -31,16 +31,17 @@ This image(s) is part of the [Docksal](http://docksal.io) image library. - Acquia Cloud API commands - drupal console launcher - terminus (Pantheon) + - platform (Platform.sh) - wp-cli - ruby - ruby - gem - bundler - nodejs - - nvm - - nodejs (via nvm) + - nodejs - npm, yarn - python +- cron Other notable tools: @@ -76,8 +77,18 @@ cli ... ``` -See [docs](https://docs.docksal.io/en/master/tools/xdebug) on using Xdebug for web and cli PHP debugging. +[See docs](https://docs.docksal.io/en/master/tools/xdebug) on using Xdebug for web and cli PHP debugging. +## Customizing Startup + +To run a custom startup script anytime the `cli` container has started, create a `startup.sh` file within the +`.docksal/services/cli` directory. Additionally, make sure that the file is executable as well so that the container +does not run into issues when attempting to execute the file. + +## Customized Cron Configuration + +Cron can be configured by making sure there is a `crontab` file located within `.docksal/services/cli`. The file should +follow the [standard crontab format](http://www.nncron.ru/help/EN/working/cron-format.htm). ## Secrets and integrations @@ -107,3 +118,10 @@ Credentials used to authenticate [Terminus](https://pantheon.io/docs/terminus) w Stored in `/home/docker/.terminus/` inside `cli`. Terminus is installed and available globally in `cli`. + +`SECRET_PLATFORMSH_CLI_TOKEN` + +Credentials used to authenticate with the [Platform.sh CLI](https://github.com/platformsh/platformsh-cli) tool. +Stored in `/home/docker/.platform` inside `cli`. + +Platform CLI is installed and available globally in `cli`. diff --git a/cloud9/Dockerfile b/cloud9/Dockerfile new file mode 100644 index 00000000..11bb7741 --- /dev/null +++ b/cloud9/Dockerfile @@ -0,0 +1,51 @@ +ARG FROM_TAG + +FROM docksal/cli:${FROM_TAG} + +# Set noninteractive mode during the build (package install) process +ARG DEBIAN_FRONTEND=noninteractive + +# Run as docker, so we don't have to fix permissions +USER docker + +ARG HOME=/home/docker +ENV C9SDK_PATH=$HOME/c9sdk +# Cloud9 IDE and dependencies +RUN \ + # Always source user profile when provisioning as user (necessary for nvm/etc. to load) + . $HOME/.profile; \ + set -xe; \ + buildDeps=" \ + g++ \ + make \ + ";\ + sudo apt-get update; \ + sudo apt-get -y --no-install-recommends install >/dev/null \ + $buildDeps \ + tmux \ + ;\ + \ + # Cloud9 installation is a mess. + # Using the installer script on its own results in "unable to install pty.js module" at runtime. + # This can be addressed by using the link.sh script, however + # link.sh fails if c9/core is installed in ~/.c9. It complains but then works fine if another directory is used (e.g. ~/c9sdk) + # So we end up with c9 stuff installed in two places (~/.c9 and ~/c9sdk), but at least it works this way. + # TODO: revise c9 installation + git clone --depth=1 https://github.com/c9/core.git ${C9SDK_PATH} >/dev/null; \ + curl -s -L https://raw.githubusercontent.com/c9/install/master/link.sh | bash; \ + ${C9SDK_PATH}/scripts/install-sdk.sh >/dev/null; \ + \ + # Cleanup + # This saves ~30MB of space. c9 throws some non-critical errors during startup (but still works). + # TODO: may need to revise this + rm -rf ${C9SDK_PATH}/.git; \ + npm cache clean --force; \ + sudo apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $buildDeps >/dev/null; \ + sudo apt-get clean; \ + sudo rm -rf /var/lib/apt/lists/*; + +# Switch back to root (IMPORTANT!) +USER root + +# Launch Cloud9 via supervisord +COPY config/supervisord-cloud9.conf /etc/supervisor/conf.d/cloud9.conf diff --git a/cloud9/Makefile b/cloud9/Makefile new file mode 100644 index 00000000..d9c49e2f --- /dev/null +++ b/cloud9/Makefile @@ -0,0 +1,57 @@ +-include ../tests/env_make +-include env_make + +VERSION ?= 7.1 +REPO = docksal/cli +FROM_TAG = build-$(VERSION) +TAG = $(FROM_TAG)-ide +NAME = docksal-cli-$(VERSION)-ide +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 --build-arg FROM_TAG=$(FROM_TAG) -t $(REPO):$(TAG) . + +test: + IMAGE=$(REPO):$(TAG) NAME=$(NAME) 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/cloud9/config/supervisord-cloud9.conf b/cloud9/config/supervisord-cloud9.conf new file mode 100644 index 00000000..ae544992 --- /dev/null +++ b/cloud9/config/supervisord-cloud9.conf @@ -0,0 +1,6 @@ +# Cloud9 IDE +[program:c9] +# Using bash -lc here to load docker user profile (necessary for nvn/node to initialize) +command = gosu docker bash -lc 'node ${C9SDK_PATH}/server.js -l 0.0.0.0 -p 3000 -w /var/www -a :' +stdout_logfile = /var/log/supervisor/c9-stdout +stderr_logfile = /var/log/supervisor/c9-stderr diff --git a/cloud9/tests/test.bats b/cloud9/tests/test.bats new file mode 100755 index 00000000..cbd9bfbd --- /dev/null +++ b/cloud9/tests/test.bats @@ -0,0 +1,83 @@ +#!/usr/bin/env bats + +# Debugging +teardown() { + echo + # TODO: figure out how to deal with this (output from previous run commands showing up along with the error message) + echo "Note: ignore the lines between \"...failed\" above and here" + echo + echo "Status: ${status}" + echo "Output:" + echo "================================================================" + echo "${output}" + echo "================================================================" +} + +# Checks container health status (if available) +# Relies on healchecks introduced in docksal/cli v1.3.0+, uses `sleep` as a fallback +# @param $1 container id/name +_healthcheck () +{ + local health_status + health_status=$(docker inspect --format='{{json .State.Health.Status}}' "$1" 2>/dev/null) + + # Wait for 5s then exit with 0 if a container does not have a health status property + # Necessary for backward compatibility with images that do not support health checks + if [[ $? != 0 ]]; then + echo "Waiting 10s for container to start..." + sleep 10 + return 0 + fi + + # If it does, check the status + 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 + local container_name="${NAME}" + local delay=5 + local timeout=30 + local elapsed=0 + + until _healthcheck "$container_name"; do + echo "Waiting for $container_name to become ready..." + 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}" + exit 1 + fi + done + + return 0 +} + +# To work on a specific test: +# run `export SKIP=1` locally, then comment skip in the test you want to debug + +@test "Cloud 9 IDE" { + [[ $SKIP == 1 ]] && skip + + ### Setup ### + make start + _healthcheck_wait + + ### Tests ### + + run make logs + echo "$output" | grep "Cloud9 is up and running" + unset output + + ### Cleanup ### + make clean +} diff --git a/scripts/docker-push.sh b/scripts/docker-push.sh new file mode 100755 index 00000000..25ca4819 --- /dev/null +++ b/scripts/docker-push.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash + +# ----- Helper functions ----- # + +is_edge () +{ + [[ "${TRAVIS_BRANCH}" == "develop" ]] +} + +is_stable () +{ + [[ "${TRAVIS_BRANCH}" == "master" ]] +} + +is_release () +{ + [[ "${TRAVIS_TAG}" != "" ]] +} + +# Check whether the current build is for a pull request +is_pr () +{ + [[ "${TRAVIS_PULL_REQUEST}" != "false" ]] +} + +is_latest () +{ + [[ "${VERSION}" == "${LATEST_VERSION}" ]] +} + +# Tag and push an image +# $1 - source image +# $2 - target image +tag_and_push () +{ + local source=$1 + local target=$2 + + # Base image + echo "Pushing ${target} image ..." + docker tag ${source} ${target} + docker push ${target} + + # Cloud9 flavor + echo "Pushing ${target}-ide image ..." + docker tag ${source}-ide ${target}-ide + docker push ${target}-ide +} + +# ---------------------------- # + +# Possible docker image tags +IMAGE_TAG_EDGE="edge-php${VERSION}" +IMAGE_TAG_STABLE="php${VERSION}" +IMAGE_TAG_RELEASE="${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 +if is_edge; then + IMAGE_TAG=${IMAGE_TAG_EDGE} +elif is_stable; then + IMAGE_TAG=${IMAGE_TAG_STABLE} +elif is_release; then + IMAGE_TAG=${IMAGE_TAG_RELEASE} +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 + echo "Pushing ${REPO}:${IMAGE_TAG_LATEST} image ..." + docker tag ${REPO}:build-${VERSION} ${REPO}:${IMAGE_TAG_LATEST} + docker push ${REPO}:${IMAGE_TAG_LATEST} +fi diff --git a/tests/.docksal/services/cli/crontab b/tests/.docksal/services/cli/crontab new file mode 100644 index 00000000..389f5c04 --- /dev/null +++ b/tests/.docksal/services/cli/crontab @@ -0,0 +1 @@ +* * * * * 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 new file mode 100755 index 00000000..1966ead3 --- /dev/null +++ b/tests/.docksal/services/cli/startup.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +echo "I ran properly" > /tmp/test-startup.txt diff --git a/tests/env_make b/tests/env_make new file mode 100644 index 00000000..845e2a55 --- /dev/null +++ b/tests/env_make @@ -0,0 +1,13 @@ +VOLUMES = \ + -v /home/docker \ + -v $(CWD)/../tests:/var/www + +ENV = \ + -e XDEBUG_ENABLED \ + -e SECRET_BLACKFIRE_CLIENT_ID \ + -e SECRET_BLACKFIRE_CLIENT_TOKEN \ + -e SECRET_SSH_PRIVATE_KEY \ + -e SECRET_ACAPI_EMAIL \ + -e SECRET_ACAPI_KEY \ + -e SECRET_PLATFORMSH_CLI_TOKEN \ + -e SECRET_TERMINUS_TOKEN diff --git a/tests/test.bats b/tests/test.bats index e53c6b25..13b0f915 100755 --- a/tests/test.bats +++ b/tests/test.bats @@ -62,10 +62,8 @@ _healthcheck_wait () return 0 } - -# Global skip -# Uncomment below, then comment skip in the test you want to debug. When done, reverse. -#SKIP=1 +# To work on a specific test: +# run `export SKIP=1` locally, then comment skip in the test you want to debug @test "Bare service" { [[ $SKIP == 1 ]] && skip @@ -101,7 +99,7 @@ _healthcheck_wait () phpInfo=$(docker exec -u docker "$NAME" php -i) output=$(echo "$phpInfo" | grep "PHP Version") - echo "$output" | grep "${PHP_VERSION}" + echo "$output" | grep "${VERSION}" unset output output=$(echo "$phpInfo" | grep "memory_limit") @@ -113,7 +111,7 @@ _healthcheck_wait () unset output # Check PHP modules - run bash -c "docker exec '${NAME}' php -m | diff php-modules.txt -" + run bash -lc "docker exec -u docker '${NAME}' php -m | diff php-modules.txt -" [[ ${status} == 0 ]] unset output @@ -121,140 +119,293 @@ _healthcheck_wait () docker rm -vf "$NAME" >/dev/null 2>&1 || true } +# Examples of using Makefile commands +# make start, make exec, make clean @test "Configuration overrides" { [[ $SKIP == 1 ]] && skip ### Setup ### - docker rm -vf "$NAME" >/dev/null 2>&1 || true - docker run --name "$NAME" -d \ - -v /home/docker \ - -v $(pwd)/../tests:/var/www \ - -e XDEBUG_ENABLED=1 \ - "$IMAGE" + make start -e ENV='-e XDEBUG_ENABLED=1' _healthcheck_wait ### Tests ### # Check PHP FPM settings overrides - run docker exec -u docker "$NAME" /var/www/scripts/test-php-fpm.sh index.php + run make exec -e CMD='/var/www/scripts/test-php-fpm.sh index.php' echo "$output" | grep "memory_limit" | grep "512M" unset output # Check xdebug was enabled - run docker exec -u docker "$NAME" php -m + run make exec -e CMD='php -m' echo "$output" | grep -e "^xdebug$" unset output # Check PHP CLI overrides - run docker exec -u docker "$NAME" php -i + run make exec -e CMD='php -i' echo "$output" | grep "memory_limit => 128M => 128M" unset output ### Cleanup ### - docker rm -vf "$NAME" >/dev/null 2>&1 || true + make clean } -@test "Check binaries and versions" { +@test "Check PHP tools and versions" { [[ $SKIP == 1 ]] && skip ### Setup ### - docker rm -vf "$NAME" >/dev/null 2>&1 || true - docker run --name "$NAME" -d \ - -v /home/docker \ - -v $(pwd)/../tests:/var/www \ - -e XDEBUG_ENABLED=1 \ - "$IMAGE" + make start _healthcheck_wait ### Tests ### # Check Composer version - run docker exec -u docker "$NAME" bash -c 'composer --version | grep "^Composer version ${COMPOSER_VERSION} "' + run docker exec -u docker "$NAME" bash -lc 'composer --version | grep "^Composer version ${COMPOSER_VERSION} "' [[ ${status} == 0 ]] unset output # Check Drush Launcher version - run docker exec -u docker "$NAME" bash -c 'drush --version | grep "^Drush Launcher Version: ${DRUSH_LAUNCHER_VERSION}$"' + run docker exec -u docker "$NAME" bash -lc 'drush --version | grep "^Drush Launcher Version: ${DRUSH_LAUNCHER_VERSION}$"' [[ ${status} == 0 ]] unset output # Check Drush version - run docker exec -u docker "$NAME" bash -c 'drush --version | grep "^ Drush Version : ${DRUSH_VERSION} $"' + run docker exec -u docker "$NAME" bash -lc 'drush --version | grep "^ Drush Version : ${DRUSH_VERSION} $"' [[ ${status} == 0 ]] unset output # Check Drupal Console version - run docker exec -u docker "$NAME" bash -c 'drupal --version | grep "^Drupal Console Launcher ${DRUPAL_CONSOLE_VERSION}$"' + run docker exec -u docker "$NAME" bash -lc 'drupal --version | grep "^Drupal Console Launcher ${DRUPAL_CONSOLE_LAUNCHER_VERSION}$"' [[ ${status} == 0 ]] unset output # Check Wordpress CLI version - run docker exec -u docker "$NAME" bash -c 'wp --version | grep "^WP-CLI ${WPCLI_VERSION}$"' + run docker exec -u docker "$NAME" bash -lc 'wp --version | grep "^WP-CLI ${WPCLI_VERSION}$"' [[ ${status} == 0 ]] unset output # Check Magento 2 Code Generator version - # TODO: this should not require running with sudo - sudo should be removed ones the following issues is addressed: - # https://github.com/staempfli/magento2-code-generator/issues/11 - run docker exec -u docker "$NAME" bash -c 'sudo mg2-codegen --version | grep "^mg2-codegen ${MG_CODEGEN_VERSION}$"' + # TODO: this needs to be replaced with the actual version check + # See https://github.com/staempfli/magento2-code-generator/issues/15 + #run docker exec -u docker "$NAME" bash -lc 'mg2-codegen --version | grep "^mg2-codegen ${MG_CODEGEN_VERSION}$"' + run docker exec -u docker "$NAME" bash -lc 'mg2-codegen --version | grep "^mg2-codegen @git-version@$"' [[ ${status} == 0 ]] unset output - # Check Blackfire CLI version - run docker exec -u docker "$NAME" bash -c 'blackfire version | grep "^blackfire ${BLACKFIRE_VERSION} "' + # Check Terminus version + run docker exec -u docker "$NAME" bash -lc 'terminus --version | grep "^Terminus ${TERMINUS_VERSION}$"' [[ ${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 Platform CLI version + run docker exec -u docker "$NAME" bash -lc 'platform --version | grep "Platform.sh CLI ${PLATFORMSH_CLI_VERSION}"' + [[ ${status} == 0 ]] unset output - # Check Terminus version - run docker exec -u docker "$NAME" bash -c 'terminus --version | grep "^Terminus ${TERMINUS_VERSION}$"' + ### Cleanup ### + make clean +} + +@test "Check NodeJS tools and versions" { + #[[ $SKIP == 1 ]] && skip + + ### Setup ### + make start + _healthcheck_wait + + ### Tests ### + + # nvm + run docker exec -u docker "$NAME" bash -lc 'nvm --version | grep "${NVM_VERSION}"' + [[ ${status} == 0 ]] + unset output + + # nodejs + run docker exec -u docker "$NAME" bash -lc 'node --version | grep "${NODE_VERSION}"' + [[ ${status} == 0 ]] + unset output + + # yarn + run docker exec -u docker "$NAME" bash -lc 'yarn --version | grep "${YARN_VERSION}"' [[ ${status} == 0 ]] unset output ### Cleanup ### - docker rm -vf "$NAME" >/dev/null 2>&1 || true + make clean } -@test "Check config templates" { +@test "Check misc tools and versions" { [[ $SKIP == 1 ]] && skip ### Setup ### - cd ../tests - echo "CLI_IMAGE=\"${IMAGE}\"" > .docksal/docksal-local.env - fin reset -f + make start + _healthcheck_wait ### Tests ### - # Load environment variables from docksal.env and confirm then are not empty - source .docksal/docksal.env + # Check Blackfire CLI version + run docker exec -u docker "$NAME" bash -lc 'blackfire version | grep "^blackfire ${BLACKFIRE_VERSION} "' + [[ ${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" + unset output + + ### Cleanup ### + make clean +} + +@test "Check config templates" { + [[ $SKIP == 1 ]] && skip + + # Source and allexport (set -a) variables from docksal.env + set -a; source $(pwd)/../tests/.docksal/docksal.env; set +a + + # Config variables were loaded [[ "${SECRET_ACAPI_EMAIL}" != "" ]] [[ "${SECRET_ACAPI_KEY}" != "" ]] [[ "${SECRET_SSH_PRIVATE_KEY}" != "" ]] + ### Setup ### + make start -e ENV="\ + -e SECRET_ACAPI_EMAIL \ + -e SECRET_ACAPI_KEY \ + -e SECRET_SSH_PRIVATE_KEY \ + " + _healthcheck_wait + + ### Tests ### + # Check Acquia Cloud API conf - run fin exec 'echo ${SECRET_ACAPI_EMAIL}' + run make exec -e CMD='echo ${SECRET_ACAPI_EMAIL}' [[ "${output}" != "" ]] unset output - run fin exec 'echo ${SECRET_ACAPI_KEY}' + run make exec -e CMD='echo ${SECRET_ACAPI_KEY}' [[ "${output}" != "" ]] unset output - run fin exec 'grep "${SECRET_ACAPI_EMAIL}" "$HOME/.acquia/cloudapi.conf" && grep "${SECRET_ACAPI_KEY}" "$HOME/.acquia/cloudapi.conf"' + # TODO: figure out how to properly use 'make exec' here (escape quotes) + run docker exec -u docker "${NAME}" bash -lc 'grep "${SECRET_ACAPI_EMAIL}" ${HOME}/.acquia/cloudapi.conf && grep "${SECRET_ACAPI_KEY}" ${HOME}/.acquia/cloudapi.conf' [[ ${status} == 0 ]] unset output # Check private SSH key - run fin exec 'echo ${SECRET_SSH_PRIVATE_KEY}' + run make exec -e CMD='echo ${SECRET_SSH_PRIVATE_KEY}' [[ "${output}" != "" ]] unset output - run fin exec 'echo "${SECRET_SSH_PRIVATE_KEY}" | diff $HOME/.ssh/id_rsa -' + # TODO: figure out how to properly use 'make exec' here (escape quotes) + run docker exec -u docker "${NAME}" bash -lc 'echo "${SECRET_SSH_PRIVATE_KEY}" | diff ${HOME}/.ssh/id_rsa -' [[ ${status} == 0 ]] unset output ### Cleanup ### - fin rm -f - rm -f .docksal/docksal-local.env + make clean +} + +@test "Check custom startup script" { + [[ $SKIP == 1 ]] && skip + + make start + _healthcheck_wait + + run docker exec -u docker "${NAME}" cat /tmp/test-startup.txt + [[ ${status} == 0 ]] + [[ "${output}" =~ "I ran properly" ]] + + ### Cleanup ### + make clean +} + +@test "Check Platform.sh integration" { + [[ $SKIP == 1 ]] && skip + + # Confirm secret is not empty + [[ "${SECRET_PLATFORMSH_CLI_TOKEN}" != "" ]] + + ### Setup ### + make start -e ENV='-e SECRET_PLATFORMSH_CLI_TOKEN' + _healthcheck_wait + + ### Tests ### + + # Confirm token was passed to the container + run docker exec -u docker "${NAME}" bash -lc 'echo SECRET_PLATFORMSH_CLI_TOKEN: ${SECRET_PLATFORMSH_CLI_TOKEN}' + [[ "${output}" == "SECRET_PLATFORMSH_CLI_TOKEN: ${SECRET_PLATFORMSH_CLI_TOKEN}" ]] + unset output + + # Confirm the SECRET_ prefix was stripped + run docker exec -u docker "${NAME}" bash -lc 'echo PLATFORMSH_CLI_TOKEN: ${SECRET_PLATFORMSH_CLI_TOKEN}' + [[ "${output}" == "PLATFORMSH_CLI_TOKEN: ${SECRET_PLATFORMSH_CLI_TOKEN}" ]] + unset output + + # Confirm authentication works + run docker exec -u docker "${NAME}" bash -lc 'platform auth:info -n' + [[ ${status} == 0 ]] + [[ ! "${output}" =~ "Invalid API token" ]] + [[ "${output}" =~ "developer@docksal.io" ]] + unset output + + ### Cleanup ### + make clean +} + +@test "Check Pantheon integration" { + [[ $SKIP == 1 ]] && skip + + # Confirm secret is not empty + [[ "${SECRET_TERMINUS_TOKEN}" != "" ]] + + ### Setup ### + make start -e ENV='-e SECRET_TERMINUS_TOKEN' + _healthcheck_wait + + ### Tests ### + + # Confirm token was passed to the container + run docker exec -u docker "${NAME}" bash -lc 'echo SECRET_TERMINUS_TOKEN: ${SECRET_TERMINUS_TOKEN}' + [[ "${output}" == "SECRET_TERMINUS_TOKEN: ${SECRET_TERMINUS_TOKEN}" ]] + unset output + + # Confirm the SECRET_ prefix was stripped + run docker exec -u docker "${NAME}" bash -lc 'echo TERMINUS_TOKEN: ${TERMINUS_TOKEN}' + [[ "${output}" == "TERMINUS_TOKEN: ${SECRET_TERMINUS_TOKEN}" ]] + unset output + + # Confirm authentication works + run docker exec -u docker "${NAME}" bash -lc 'terminus auth:whoami' + [[ ${status} == 0 ]] + [[ ! "${output}" =~ "You are not logged in." ]] + [[ "${output}" =~ "developer@docksal.io" ]] + unset output + + ### Cleanup ### + make clean +} + +@test "Check cron" { + [[ $SKIP == 1 ]] && skip + + ### 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}" == "" ]] + unset output + + # Sleep for 60 seconds so cron can run again. + sleep 60 + + # Confirm cron has ran 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 + + ### Cleanup ### + make clean }