diff --git a/.gitignore b/.gitignore index e9ee7f7..59ba901 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,10 @@ VVV +/.vagrant +/log +/www +/git +/src/coretech +/src/wp-themes +/src/wp-core/wordpress +/src/wp-core/wp-tests +/config/.ssh \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a98d8f9 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vvv"] + path = vvv + url = git@github.com:Varying-Vagrant-Vagrants/VVV.git diff --git a/Customfile b/Customfile new file mode 100644 index 0000000..ba07b78 --- /dev/null +++ b/Customfile @@ -0,0 +1,133 @@ +pmc_vvv=File.realdirpath( '..' ); + +require File.join( pmc_vvv,'lib','functions.rb' ) + +# custom mapping for pmc specific improvement +config.vm.synced_folder File.join(pmc_vvv, 'config/.ssh'), '/srv/.ssh', owner: 'vagrant', group: 'root', mount_options: ['dmode=775', 'fmode=600'], automount: true, SharedFoldersEnableSymlinksCreate: true +config.vm.synced_folder File.join(pmc_vvv, 'src'), '/srv/src', owner: 'vagrant', group: 'www-data', mount_options: ['dmode=775', 'fmode=774'], automount: true, SharedFoldersEnableSymlinksCreate: true +config.vm.synced_folder pmc_vvv, '/srv/pmc-vvv', owner: 'vagrant', group: 'www-data', mount_options: ['dmode=775', 'fmode=774'], automount: true, SharedFoldersEnableSymlinksCreate: true + +# we want to override some default from VVV to settings we know work properly from pmc-qs7 +config.vm.provider :virtualbox do |v| + + # rename the VM since we are customized the setting to PMC specificiation + v.name = "pmc-vvv_" + (Digest::SHA256.hexdigest pmc_vvv)[0..10] + + # Allow the VM to utilize up to 1/2 of the host cpu cores + v.cpus = detect_max_cpus 2, 0.5 + + # Allow the VM to utilize up to 3/4 of the host system memory + v.memory = detect_max_mem 4096, 0.75 + + # Only allow the VM to use up-to 50% of the host system cpu + # Windows OS, the server may hang if cpu allow to reach 100% + v.customize ["modifyvm", :id, "--cpuexecutioncap", "50"] + + # Instruct Vagrant to utilize all the cpu cores we've provided above + # Without this, only 1 core will be used + v.customize ["modifyvm", :id, "--ioapic", "on"] + + # Plug in the virtual network adapter as if it were a real cat-5 cable + # plugged into a real network card. This corrects an error presented with + # Ubuntu 16 where boot hangs on: "A start job is running for Raise network interfaces" + # This error occurs because Ubuntu tries to raise all the network interfaces, + # but the 'cable' isn't connected, so it waits until the timeout. + # Note, this is the same as selecting 'Cable Connect' for the network adapter + # in the VirtualBox app settings for this box. + # https://github.com/hashicorp/vagrant/issues/8056#issuecomment-267600935 + v.customize ["modifyvm", :id, "--cableconnected1", "on"] + + # Turn off un-use devices + v.customize ["modifyvm", :id, "--usb", "off"] + v.customize ["modifyvm", :id, "--usbehci", "off"] + v.customize ["modifyvm", :id, "--usbxhci", "off"] + v.customize ["modifyvm", :id, "--accelerate3d", "off"] + v.customize ["modifyvm", :id, "--accelerate2dvideo", "off"] + v.customize ["modifyvm", :id, "--audio", "none"] + v.customize ["modifyvm", :id, "--vrde", "off"] + +end + +# In order to add files to /etc folder, we need to access the privileged script to do the copy +# First we need to add the source to a temporarly folder +config.vm.provision "pmc-file", + run: 'always', + type: "file", + source: File.join(pmc_vvv, 'config/etc/.'), + destination: "/tmp/etc/" +# We then execute a shell script inside VM with admin privilege to copy the files +config.vm.provision "pmc-script", + run: 'always', + after: "pmc-file", + type: "shell", + privileged: true, + inline: <<-SHELL + rsync -r /tmp/etc/ /etc/ + rm -rf /tmp/pmc-etc + mkdir -p /vagrant + mkdir -p /vagrant/failed_provisioners + cp /srv/.ssh/config /root/.ssh/config + cp /srv/.ssh/config /home/vagrant/.ssh/config + cp /srv/config/config.yml /vagrant/config.yml + chmod 600 /root/.ssh/config /home/vagrant/.ssh/config + chown root:root /root/.ssh/config + chown vagrant:vagrant /home/vagrant/.ssh/config + SHELL + +# Implementing PMC specific provision for each site that can't be done by common provision template +$vvv_config['sites'].each do |site, args| + next if args['skip_provisioning'] + + # In order to properly develop and test provision script directly on local, + # we need to override and map the custom-site-template folder + if Dir.exist?(File.join(pmc_vvv, 'git/custom-site-template')) + + # override and disable remote repo since we want use our local template + $vvv_config['sites'][site]['repo'] = false + + # map the custom provision script directly into the folder so we can develop the scripts without copying between the folders + config.vm.synced_folder File.join(pmc_vvv, 'git/custom-site-template/provision'), '/srv/www/' + site + '/provision', owner: 'vagrant', group: 'www-data', mount_options: ['dmode=775', 'fmode=774'], automount: true, SharedFoldersEnableSymlinksCreate: true + end + + # VIP GO/Classic + if args['custom'] && args['custom']['wp_vip'] && File.exist?(File.join(pmc_vvv, 'provision/provision-bind-mount.sh')) + + # We could let's vagrant auto map the shared folder, + # however the VVV site provision script doesn't support remote git repo checkout into non-empty folder + + # provision after dependencies doesn't work :( + # let's work some magic to bypass vvv's site provision + $vvv_config['sites'][site]['skip_provisioning'] = true + config.vm.provision "site-#{site}", + type: 'shell', + keep_color: true, + path: File.join('provision', 'provision-site.sh'), + args: [ + site, + args['repo'].to_s, + args['branch'], + args['vm_dir'], + args['skip_provisioning'].to_s, + args['nginx_upstream'] + ], + env: { "VVV_LOG" => "site-#{site}" } + + config.vm.provision "bind-mount-#{site}", + after: "site-#{site}", + run: 'always', + type: 'shell', + keep_color: true, + path: File.join(pmc_vvv, 'provision', 'provision-bind-mount.sh'), + args: [ + site, + args['vm_dir'], + args['custom']['wp_vip'], + args['custom']['share_code'].to_s + ], + env: { "VVV_LOG" => "bind-mount-#{site}" } + + end + +end + + diff --git a/Vagrantfile b/Vagrantfile new file mode 100644 index 0000000..9a37f20 --- /dev/null +++ b/Vagrantfile @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +# -*- mode: ruby -*- +# vi: set ft=ruby ts=2 sw=2 et: +Vagrant.require_version '>= 2.2.4' + +# Define constant for vvv folder +VVV='vvv' + +require "getoptlong" +require 'fileutils' +require 'open3' +require "readline" +require 'yaml' +require File.join( __dir__,'lib','functions.rb' ) + +# Auto install all required plugins +install_required_plugins() + +FileUtils.mkdir_p("./src/coretech/pmc-plugins") unless Dir.exist?("./src/coretech/pmc-plugins") +FileUtils.mkdir_p("./src/coretech/pmc-core-v2") unless Dir.exist?("./src/coretech/pmc-core-v2") +FileUtils.cp( File.join( 'provision', 'provision-post.sh' ), File.join( 'vvv', 'provision', 'provision-post.sh' ) ); + +# Copy our config.yml to vvv folder, currently there is no way for us to use a different config file from different location + +# development testing for now +FileUtils.cp( 'config-tests.yml', File.join( 'vvv', 'config', 'config.yml' ) ); +FileUtils.cp( 'Customfile', File.join( 'vvv', 'Customfile' ) ); + +ENV['VVV_SKIP_LOGO'] = 'true' +Dir.chdir 'vvv' +exec "vagrant #{ARGV.join(' ')}" +Kernel.exit!(0) diff --git a/config-tests.yml b/config-tests.yml new file mode 100644 index 0000000..404e07d --- /dev/null +++ b/config-tests.yml @@ -0,0 +1,81 @@ +--- +pmc: + share: + skip_provisioning: true + php_version: 7.3 + wp_version: 5.4 + coretech_dir: /srv/src/coretech + wp_core_dir: /srv/src/wp-core + wp_themes_dir: /srv/src/wp-themes + +sites: + + wpcom: + skip_provisioning: true + repo: https://github.com/hauvong/custom-site-template.git + hosts: + - wpcom.test + - bgr.wpcom.test + - pmc-corporate-2018.wpcom.test + - pmc-deadline-2019.wpcom.test + - pmc-footwearnews-2018.wpcom.test + - pmc-hollywoodlife-2017.wpcom.test + - pmc-spy.wpcom.test + - pmc-summits.wpcom.test + - pmc-summits-rr1.wpcom.test + - pmc-summits-variety.wpcom.test + - pmc-summits-wwd.wpcom.test + - pmc-summits.wpcom.test + - pmc-tvline.wpcom.test + - pmc-variety-2017.wpcom.test + - pmc-wwd-2016.wpcom.test + - pmc-variety-2020.wpcom.test + custom: + wp_type: single + wp_vip: classic + wp_tests: true + wp_version: 5.4 + wp_themes: + vip/plugins: + repo: https://bitbucket.org/penskemediacorp/wordpress-vip-plugins.git + + vipgo: + skip_provisioning: false + repo: https://github.com/hauvong/custom-site-template.git + hosts: + - vipgo.test + - rollingstone.vipgo.test + - artnews.vipgo.test + - goldderby.vipgo.test + - sheknows.vipgo.test + - soaps.vipgo.test + custom: + wp_type: single + wp_vip: vipgo + wp_tests: true + wp_version: 5.4 + wp_plugins: + repo: git@bitbucket.org:penskemediacorp/pmc-vip-go-plugins.git + +utilities: + core: + - memcached-admin +# - mongodb + - opcache-status + - php73 + - phpmyadmin +# - tideways + - tls-ca + - webgrind + +vm_config: + cores: 4 + memory: 4096 + +general: +# db_backup: true +# db_restore: true + db_share_type: false + +vagrant-plugins: + disksize: 10GB diff --git a/config.yml b/config.yml index e8d42d6..7d070d0 100644 --- a/config.yml +++ b/config.yml @@ -1,4 +1,13 @@ --- +pmc: + share: + skip_provisioning: true + php_version: 7.3 + wp_version: 5.4 + coretech_dir: /srv/src/coretech + wp_core_dir: /srv/src/wp-core + wp_themes_dir: /srv/src/wp-themes + sites: wpcom: skip_provisioning: false @@ -22,6 +31,8 @@ sites: - pmc-variety-2020.wpcom.test custom: wp_type: subdomain + wp_tests: true + # VIPGO sites: # @NOTE: name === theme-name && host @@ -370,6 +381,7 @@ utilities: - mongodb - opcache-status - php74 + - php73 - phpmyadmin - tideways - tls-ca diff --git a/config/.ssh/config b/config/.ssh/config new file mode 100644 index 0000000..0ad8b66 --- /dev/null +++ b/config/.ssh/config @@ -0,0 +1,4 @@ +StrictHostKeyChecking no +UserKnownHostsFile=/dev/null +IdentityFile /srv/.ssh/bitbucket-id_rsa +IdentityFile /srv/.ssh/github-id_rsa diff --git a/config/etc/environment b/config/etc/environment new file mode 100644 index 0000000..fe097c5 --- /dev/null +++ b/config/etc/environment @@ -0,0 +1,5 @@ +PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/srv/config/homebin +XDEBUG_REMOTE_HOST=10.0.2.2 +PMC_PHPUNIT_BOOTSTRAP=/srv/src/wp-core/wp-config/bootstrap-phpunit.php +WP_TESTS_DIR=/srv/src/wp-core/wp-tests/phpunit/ +WP_CONFIG_BOOTSTRAP=true diff --git a/lib/functions.rb b/lib/functions.rb new file mode 100644 index 0000000..6578dec --- /dev/null +++ b/lib/functions.rb @@ -0,0 +1,77 @@ +$logger = Vagrant::UI::Colored.new + +#require File.join( __dir__,'functions-git.rb' ) + +def install_required_plugins() + + # vagrant-persistent-storage + # vagrant-git + # vagrant-docker-compose + + required_plugins = %w( rb-readline vagrant-ghost vagrant-vbguest vagrant-hostsupdater vagrant-disksize ) + missing_plugins = [] + required_plugins.each do |plugin| + missing_plugins.push(plugin) unless Vagrant.has_plugin? plugin + end + if ! missing_plugins.empty? + install_these = missing_plugins.join(' ') + $logger.warn "Required following plugins: #{install_these}." + if system "vagrant plugin install #{install_these}" + exec "vagrant #{ARGV.join(' ')}" + Kernel.exit!(0) + else + $logger.warn "Error install plugins, please install these plugins then restart vagrant:" + $logger.warn " #{install_these}" + Kernel.exit!(0) + end + end + +end + +# @see https://github.com/psudug/nittany-vagrant/blob/master/Vagrantfile +def detect_max_cpus( default = 1, multiplier = 1 ) + host_os = RbConfig::CONFIG['host_os'] + cpus = 0 + + # Give VM 1/4 system memory & access to all cpu cores on the host + if host_os =~ /darwin/ + cpus = `sysctl -n hw.ncpu`.to_i + elsif host_os =~ /linux/ + cpus = `nproc`.to_i + elsif host_os =~ /mingw32/ + cpus = `wmic cpu get NumberOfLogicalProcessors | grep '^[0-9]'`.to_i + end + + if cpus <= 0 + return default + end + + return (cpus * multiplier).round +end + +# @see https://github.com/psudug/nittany-vagrant/blob/master/Vagrantfile +def detect_max_mem( default = 1024, multiplier = 1 ) + host_os = RbConfig::CONFIG['host_os'] + mem = 0 + + # Give VM 1/4 system memory & access to all cpu cores on the host + if host_os =~ /darwin/ + # sysctl returns Bytes and we need to convert to MB + mem = `sysctl -n hw.memsize`.to_i / 1024 / 1024 + elsif host_os =~ /linux/ + # meminfo shows KB and we need to convert to MB + mem = `grep 'MemTotal' /proc/meminfo | sed -e 's/MemTotal://' -e 's/ kB//'`.to_i / 1024 + elsif host_os =~ /mingw32/ + mem = `wmic os get TotalVisibleMemorySize | grep '^[0-9]'`.to_i / 1024 + if mem < 1024 + mem = 1024 + end + end + + if mem <= 0 + return default + end + + return (mem * multiplier).round +end + diff --git a/provision/provision-bind-mount.sh b/provision/provision-bind-mount.sh new file mode 100644 index 0000000..6ebee62 --- /dev/null +++ b/provision/provision-bind-mount.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash + +set -eo pipefail + +# We need this script to bind mount various folder +# avoid using symlink where wordpress may not work properly with symlink. +# +# VIP Go +# wp-content/plugins/pmc-plugins -> coretech/pmc-plugins +# wp-content/themes/pmc-core-v2 -> coretech/pmc-core-v2 +# wp-content/themes/vip/pmc-core-v2 -> coretech/pmc-core-v2 +# +# VIP Classic +# wp-content/themes/vip/pmc-plugins -> coretech/pmc-plugins +# wp-content/themes/vip/pmc-core-v2 -> coretech/pmc-core-v2 +# +# If share code is enabled, the following structures are used +# +# VIP Go +# wp-content/plugins/pmc-plugins -> coretech/pmc-plugins +# wp-content/themes/pmc-core-v2 -> coretech/pmc-core-v2 +# wp-content/themes/vip/pmc-core-v2 -> coretech/pmc-core-v2 +# wp-content/themes -> pmc_shared/wp-themes +# +# VIP Classic +# wp-content/themes/vip -> pmc_shared/wp-themes +# wp-content/themes/vip/pmc-plugins -> coretech/pmc-plugins +# wp-content/themes/vip/pmc-core-v2 -> coretech/pmc-core-v2 +# +# @TODO +# public_html/wordpress -> /srv/src/wp-core/wordpress-[version] +# public_html/wp-tests/phpunit/includes -> /srv/src/wp-core/wp-tests-[version]/phpunit/includes +# + +. "/srv/provision/provisioners.sh" + +SITE=$1 +SITE_ESCAPED="${SITE//./\\.}" +VM_DIR=$2 +WP_VIP=$(get_config_value "sites.${SITE_ESCAPED}.custom.wp_vip" false) +PMC_SHARE_CORETECH_DIR=$(get_config_value "pmc.share.coretech_dir" "/srv/www/pmc/coretech") +PMC_SHARE_WP_THEMES_DIR=$(get_config_value "pmc.share.wp_themes_dir" '') + +echo "Setup bind mount for site ${SITE}: ${WP_VIP}" + +WP_CORE_DIR="${VM_DIR}/public_html" +WP_CONTENT_DIR="${WP_CORE_DIR}/wp-content" +WP_PLUGINS_DIR="${WP_CONTENT_DIR}/plugins" +WP_MU_PLUGINS_DIR="${WP_CONTENT_DIR}/mu-plugins" + +function bind_mount() { + local SOURCE=$1 + local TARGET=$2 + echo "bind mount: ${SOURCE} ${TARGET}" + if [[ -n "$(mount | grep ${TARGET})" ]]; then + umount ${TARGET} + fi + noroot mkdir -p ${SOURCE} ${TARGET} + mount --bind ${SOURCE} ${TARGET} +} + +function bind_mount_folders() { + local SOURCE=$1 + local TARGET=$2 + local FOLDER + for d in ${SOURCE}/*/; { + FOLDER=$(basename "${d%/}") + if [[ -n "${FOLDER}" && "*" != "${FOLDER}" && -d ${SOURCE}/${FOLDER} ]]; then + bind_mount "${SOURCE}/${FOLDER}" "${TARGET}/${FOLDER}" + fi + } +} + +# IMPORTANT: We need to bind the themes folder first, +# otherwise the pmc-core-v2 or pmc-plugins won't bind properly + +if [[ -n "${PMC_SHARE_WP_THEMES_DIR}" ]]; then + if [[ "${WP_VIP}" =~ go ]]; then + bind_mount_folders ${PMC_SHARE_WP_THEMES_DIR} ${WP_CONTENT_DIR}/themes + elif [[ "${WP_VIP}" =~ classic ]]; then + bind_mount_folders ${PMC_SHARE_WP_THEMES_DIR} ${WP_CONTENT_DIR}/themes/vip + fi +fi + +bind_mount ${PMC_SHARE_CORETECH_DIR}/pmc-core-v2 ${WP_CONTENT_DIR}/themes/vip/pmc-core-v2 + +if [[ "${WP_VIP}" =~ go ]]; then + bind_mount ${PMC_SHARE_CORETECH_DIR}/pmc-plugins ${WP_PLUGINS_DIR}/pmc-plugins + bind_mount ${PMC_SHARE_CORETECH_DIR}/pmc-core-v2 ${WP_CONTENT_DIR}/themes/pmc-core-v2 +elif [[ "${WP_VIP}" =~ classic ]]; then + bind_mount ${PMC_SHARE_CORETECH_DIR}/pmc-plugins ${WP_CONTENT_DIR}/themes/vip/pmc-plugins +fi + +provisioner_success \ No newline at end of file diff --git a/provision/provision-coretech.sh b/provision/provision-coretech.sh new file mode 100644 index 0000000..53b2346 --- /dev/null +++ b/provision/provision-coretech.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +set -eo pipefail + +. "/srv/provision/provisioners.sh" + +provisioner_begin "coretech" + +PMC_SHARE_CORETECH_DIR=$(get_config_value "pmc.share.coretech_dir" "/srv/www/pmc/coretech") +PMC_SHARE_PHP_VERSION=$(get_config_value "pmc.share.php_version" "7.3") + +function git_checkout { + local TARGET=${1} + local SOURCE=${2} + + if [[ -z "${TARGET}" ]]; then + exit 1 + fi + if [[ -z "${SOURCE}" ]]; then + exit 1 + fi + + if [ ! -d ${TARGET} ] + then + noroot mkdir -p ${TARGET} + fi + + echo "Git checkout: ${SOURCE} ${TARGET}" + + local OLD_PATH="$(pwd)" + cd ${TARGET} + + if [ ! -d ${TARGET}/.git ]; then + noroot git clone ${SOURCE} . + else + noroot git pull + fi + + cd ${OLD_PATH} + +} + +if [ -f /usr/bin/php${PMC_SHARE_PHP_VERSION} ]; then + update-alternatives --set php /usr/bin/php${PMC_SHARE_PHP_VERSION} + update-alternatives --set phar /usr/bin/phar${PMC_SHARE_PHP_VERSION} + update-alternatives --set phar.phar /usr/bin/phar.phar${PMC_SHARE_PHP_VERSION} + update-alternatives --set phpize /usr/bin/phpize${PMC_SHARE_PHP_VERSION} + update-alternatives --set php-config /usr/bin/php-config${PMC_SHARE_PHP_VERSION} +fi + +git_checkout ${PMC_SHARE_CORETECH_DIR}/pmc-plugins git@bitbucket.org:penskemediacorp/pmc-plugins.git +git_checkout ${PMC_SHARE_CORETECH_DIR}/pmc-core-v2 git@bitbucket.org:penskemediacorp/pmc-core-v2.git + +git_checkout ${PMC_SHARE_CORETECH_DIR}/pmc-codesniffer git@bitbucket.org:penskemediacorp/pmc-codesniffer.git +cd ${PMC_SHARE_CORETECH_DIR}/pmc-codesniffer +noroot composer install --no-autoloader +noroot composer depends squizlabs/php_codesniffer + +ln -sf ${PMC_SHARE_CORETECH_DIR}/pmc-codesniffer/vendor/squizlabs/php_codesniffer/bin/phpcbf /usr/local/bin/ +ln -sf ${PMC_SHARE_CORETECH_DIR}/pmc-codesniffer/vendor/squizlabs/php_codesniffer/bin/phpcs /usr/local/bin/ +chmod +x /usr/local/bin/* +noroot phpcs --config-set default_standard PmcWpVip + +if [[ -z "$(grep "self::getConfigData('show_sources')" vendor/squizlabs/php_codesniffer/src/Config.php)" ]]; then + sed "/self::getConfigData('show_progress')/i\$showSources=self::getConfigData('show_sources');if(\$showSources!==null){\$this->showSources=(bool)\$showSources;}" -i vendor/squizlabs/php_codesniffer/src/Config.php + echo "Patched phpcs" +fi +phpcs --config-set show_sources 1 + +provisioner_success \ No newline at end of file diff --git a/provision/provision-pmc-share.sh b/provision/provision-pmc-share.sh new file mode 100644 index 0000000..568020c --- /dev/null +++ b/provision/provision-pmc-share.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +set -eo pipefail + +. "/srv/provision/provisioners.sh" +provisioner_begin "pmc-share" + +SKIP_PROVISION=$(get_config_value "pmc.share.skip_provisioning") +if [[ -n "${SKIP_PROVISION}" && "true" == "${SKIP_PROVISION}" ]]; then + exit +fi + +PMC_SHARE_WP_CORE_DIR=$(get_config_value "pmc.share.wp_core_dir" "/srv/src/wp-core") +PMC_SHARE_WP_VERSION=$(get_config_value "pmc.share.wp_version" "5.4") + +WP_CORE_DIR="${PMC_SHARE_WP_CORE_DIR}/wordpress" +WP_CONTENT_DIR="${WP_CORE_DIR}/wp-content" +WP_PLUGINS_DIR="${WP_CONTENT_DIR}/plugins" +WP_MU_PLUGINS_DIR="${WP_CONTENT_DIR}/mu-plugins" +WP_TESTS_DIR="${PMC_SHARE_WP_CORE_DIR}/wp-tests" +WP_TESTS_CONFIG="${WP_TESTS_DIR}/phpunit/wp-tests-config.php" +WP_TESTS_DB_NAME="wp_tests" + +if [[ "latest" == "${PMC_SHARE_WP_VERSION}" ]]; then + WP_VERSION=$( curl -s http://api.wordpress.org/core/version-check/1.7/ | awk 'match($0, /"version":"([^"]+)"/, v) { print v[1]}' ) +else + WP_VERSION="${PMC_SHARE_WP_VERSION}" +fi + +if [[ ! -d ${WP_CORE_DIR} ]]; then + echo " * Installing Wordpress ${WP_VERSION} ${WP_CORE_DIR}" + curl -sL https://wordpress.org/wordpress-${WP_VERSION}.tar.gz | noroot tar -zx --directory ${PMC_SHARE_WP_CORE_DIR} + if [[ "${PMC_SHARE_WP_CORE_DIR}/wordpress" != "${WP_CORE_DIR}" ]]; then + noroot mv ${PMC_SHARE_WP_CORE_DIR}/wordpress ${WP_CORE_DIR} + fi + echo " * Wordpress installed" +fi + +if [[ ! -d ${WP_TESTS_DIR}/phpunit/includes ]]; then + echo " * Installing Wordpress Tests ${WP_VERSION} ${WP_TESTS_DIR}" + noroot svn co --quiet https://develop.svn.wordpress.org/tags/${WP_VERSION}/tests/phpunit/includes ${WP_TESTS_DIR}/phpunit/includes + noroot svn --force export https://develop.svn.wordpress.org/tags/${WP_VERSION}/wp-tests-config-sample.php ${WP_TESTS_CONFIG} + + sed \ + -e "s|dirname( __FILE__ ) . '/src/'|'${WP_CORE_DIR}/'|" \ + -e "s/youremptytestdbnamehere/${WP_TESTS_DB_NAME}/" \ + -e "s/yourpasswordhere/wp/" \ + -e "s/yourusernamehere/wp/" \ + -i ${WP_TESTS_CONFIG} + + echo -e " * Creating database '${WP_TESTS_DB_NAME}' (if it's not already there)" + mysql -u root --password=root -e "CREATE DATABASE IF NOT EXISTS \`${WP_TESTS_DB_NAME}\`" + echo -e " * Granting the wp user priviledges to the '${WP_TESTS_DB_NAME}' database" + mysql -u root --password=root -e "GRANT ALL PRIVILEGES ON \`${WP_TESTS_DB_NAME}\`.* TO wp@localhost IDENTIFIED BY 'wp';" + echo -e " * DB operations done." + + echo " * Wordpress Tests installed." +fi + +provisioner_success \ No newline at end of file diff --git a/provision/provision-post.sh b/provision/provision-post.sh new file mode 100644 index 0000000..916468f --- /dev/null +++ b/provision/provision-post.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +. /srv/pmc-vvv/provision/provision-coretech.sh +. /srv/pmc-vvv/provision/provision-pmc-share.sh + +VVV_PROVISIONER_RUNNING=post +provisioner_success diff --git a/provision/provision-tests.sh b/provision/provision-tests.sh new file mode 100644 index 0000000..7463cd9 --- /dev/null +++ b/provision/provision-tests.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash + +set -eo pipefail + +. "/srv/provision/provisioners.sh" +function get_config_value() { + local value=$(shyaml get-value "sites.${SITE_ESCAPED}.custom.${1}" 2> /dev/null < ${VVV_CONFIG}) + echo "${value:-$2}" +} +function get_primary_host() { + local value=$(shyaml get-value "sites.${SITE_ESCAPED}.hosts.0" 2> /dev/null < ${VVV_CONFIG}) + echo "${value:-$1}" +} + +SITE=$1 +SITE_ESCAPED="${SITE//./\\.}" +VM_DIR=$2 +VVV_PATH_TO_SITE=${VM_DIR} # used in site templates +VVV_SITE_NAME=${SITE} + +echo " * Test Suite provisioner ${VVV_SITE_NAME}" + +DOMAIN=$(get_primary_host "${VVV_SITE_NAME}".test) +SITE_TITLE=$(get_config_value 'site_title' "${DOMAIN}") +WP_VERSION=$(get_config_value 'wp_version' 'latest') +DB_NAME=$(get_config_value 'db_name' "${VVV_SITE_NAME}") +DB_NAME="${DB_NAME//[\\\/\.\<\>\:\"\'\|\?\!\*]/}" + +WP_CORE_DIR="${VVV_PATH_TO_SITE}/public_html" + +install_test_suite() { + + WP_TESTS=$(get_config_value 'wp_tests.provision' false | awk '{print tolower($0)}') + if [ "${WP_TESTS}" != "true" ]; then + WP_TESTS=$(get_config_value 'wp_tests' false | awk '{print tolower($0)}') + if [ "${WP_TESTS}" != "true" ]; then + return 0 + fi + fi + WP_TESTS_VERSION=${WP_VERSION} + WP_TESTS_DATA=$(get_config_value 'wp_tests.data' false | awk '{print tolower($0)}') + WP_TESTS_DB_NAME=$(get_config_value 'wp_tests.db_name' "${DB_NAME}_tests") + WP_TESTS_DIR=$(get_config_value 'wp_tests.dir' "${VVV_PATH_TO_SITE}/wp-tests") + WP_TESTS_CONFIG="${WP_TESTS_DIR}/wp-tests-config.php" + + if [[ "nightly" == "${VERSION}" || "trunk" == "${WP_TESTS_VERSION}" ]]; then + WP_TESTS_TAG="trunk" + elif [[ "latest" == "${WP_TESTS_VERSION}" ]]; then + WP_TESTS_VERSION=$( curl -s http://api.wordpress.org/core/version-check/1.7/ | awk 'match($0, /"version":"([^"]+)"/, v) { print v[1]}' ) + if [[ -n "${WP_TESTS_VERSION}" ]]; then + WP_TESTS_TAG="tags/${WP_TESTS_VERSION}" + fi + else + WP_TESTS_TAG="tags/${WP_TESTS_VERSION}" + fi + + if [[ -n "${WP_TESTS_TAG}" ]]; then + + if [[ ! -d ${WP_TESTS_DIR}/phpunit/includes ]]; then + echo " * Installing Wordpress Tests ${WP_TESTS_VERSION} ${WP_TESTS_DIR}" + noroot svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes ${WP_TESTS_DIR}/phpunit/includes + if [[ "true" == "${WP_TESTS_DATA}" ]]; then + noroot svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ ${WP_TESTS_DIR}/phpunit/data + fi + else + echo " * Updating Wordpress Tests ${WP_TESTS_VERSION} ${WP_TESTS_DIR}" + noroot svn up ${WP_TESTS_DIR}/phpunit/includes + if [[ "true" == "${WP_TESTS_DATA}" ]]; then + noroot svn up ${WP_TESTS_DIR}/phpunit/data + fi + fi + + noroot svn --force export https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php ${WP_TESTS_DIR}/wp-tests-config.php + + sed \ + -e "s|dirname( __FILE__ ) . '/src/'|'${WP_DIR}/'|" \ + -e "s|Test\ Blog|${SITE_TITLE}|" \ + -e "s|admins@example\.org|${ADMIN_EMAIL}|" \ + -e "s|example\.org|${DOMAIN}|" \ + -e "s/youremptytestdbnamehere/${WP_TESTS_DB_NAME}/" \ + -e "s/yourpasswordhere/wp/" \ + -e "s/yourusernamehere/wp/" \ + -i ${WP_TESTS_CONFIG} + + echo -e " * Creating database '${WP_TESTS_DB_NAME}' (if it's not already there)" + mysql -u root --password=root -e "CREATE DATABASE IF NOT EXISTS \`${WP_TESTS_DB_NAME}\`" + echo -e " * Granting the wp user privileges to the '${WP_TESTS_DB_NAME}' database" + mysql -u root --password=root -e "GRANT ALL PRIVILEGES ON \`${WP_TESTS_DB_NAME}\`.* TO wp@localhost IDENTIFIED BY 'wp';" + echo -e " * DB operations done." + + fi + +} + +install_test_suite + +echo " * Test Suite provisioner script completed for ${VVV_SITE_NAME}" + +provisioner_success \ No newline at end of file diff --git a/src/wp-core/wp-config/bootstrap-phpunit.php b/src/wp-core/wp-config/bootstrap-phpunit.php new file mode 100644 index 0000000..c41e708 --- /dev/null +++ b/src/wp-core/wp-config/bootstrap-phpunit.php @@ -0,0 +1,124 @@ +_bootstrap = $matches[0] . 'pmc-unit-test/bootstrap.php'; + } + + parent::__construct(); + + $theme_folder = dirname( $this->site_info->folder ); + if ( empty( $this->_bootstrap ) ) { + if ( file_exists( $theme_folder . '/pmc-plugins/pmc-unit-test/bootstrap.php' ) ) { + $this->_bootstrap = $theme_folder . '/pmc-plugins/pmc-unit-test/bootstrap.php'; + } else { + if ( preg_match( '#^.*/wp-content#', $this->site_info->folder, $matches ) ) { + if ( file_exists( $matches[0] . '/plugins/pmc-plugins/pmc-unit-test/bootstrap.php' ) ) { + $this->_bootstrap = $matches[0] . '/plugins/pmc-plugins/pmc-unit-test/bootstrap.php'; + } + } + if ( empty( $this->_bootstrap ) ) { + if ( file_exists( '/srv/www/pmc/coretech/pmc-plugins/pmc-unit-test/bootstrap.php' ) ) { + $this->_bootstrap = '/srv/www/pmc/coretech/pmc-plugins/pmc-unit-test/bootstrap.php'; + } + } + } + } + + if ( ! empty( $this->site_info->wp_tests_folder ) ) { + $phpunit_dir = $this->site_info->wp_tests_folder; + } + + if ( empty( $phpunit_dir ) || ! file_exists( $phpunit_dir ) ) { + + $phpunit_dir = false; + if ( preg_match( '#(.*)/[^/]+/wp-content/#', $theme_folder, $matches ) ) { + if ( file_exists( $matches[1] . '/wp-tests/phpunit' ) ) { + $phpunit_dir = $matches[1] . '/wp-tests/phpunit'; + } + } + $check_dir = $theme_folder; + $check_level = 10; + while ( $check_level > 0 && !$phpunit_dir && !empty( $check_dir ) && ! in_array( $check_dir, [ '.', '/' ], true ) ) { + $check_level -= 1; + $check_dir = dirname( $check_dir ); + if ( in_array( $check_dir, [ '.', '/' ], true ) ) { + break; + } + if ( file_exists( $check_dir . '/wp-tests/phpunit' ) ) { + $phpunit_dir = $check_dir . '/wp-tests/phpunit'; + } + } + + + } + + if ( empty( $phpunit_dir ) || ! file_exists( $phpunit_dir ) ) { + $phpunit_dir = getenv( 'WP_TESTS_DIR' ); + } + + if ( empty( $phpunit_dir ) || ! file_exists( $phpunit_dir ) ) { + throw new \Error( 'Cannot auto detect location for wp-tests folder' ); + } + putenv( 'WP_TESTS_DIR=' . $phpunit_dir ); + + if ( file_exists( $phpunit_dir . '/wp-tests-config.php' ) ) { + ifndef( 'WP_TESTS_CONFIG_FILE_PATH', realpath( $phpunit_dir . '/wp-tests-config.php' ) ); + } + elseif ( file_exists( $phpunit_dir . '/../wp-tests-config.php' ) ) { + ifndef( 'WP_TESTS_CONFIG_FILE_PATH', realpath( $phpunit_dir . '/../wp-tests-config.php' ) ); + } + + } + + /** + * phpunit test should always active once it is manually referenced + */ + public function is_active() { + return true; + } + + public function start() { + if ( empty( $this->_bootstrap ) ) { + throw new \Error( sprintf('Cannot auto detect pmc plugin bootstrap file location' ) ); + } + if ( ! file_exists( $this->_bootstrap ) ) { + throw new \Error( sprintf('Cannot locate bootstrap file: %s', $this->_bootstrap ) ); + } + require_once $this->_bootstrap; + } +} + +Bootstrap_Phpunit::get_instance()->start(); diff --git a/src/wp-core/wp-config/bootstrap.php b/src/wp-core/wp-config/bootstrap.php new file mode 100644 index 0000000..e358ed6 --- /dev/null +++ b/src/wp-core/wp-config/bootstrap.php @@ -0,0 +1,244 @@ +is_active() ) { + return; + } + + $this->define_constants(); + + } + + /** + * Determine if wp configuration bootstrap is active or not + * @return bool + */ + public function is_active() { + $use_bootstrap = getenv( 'WP_CONFIG_BOOTSTRAP' ); + return ( ! empty( $use_bootstrap ) && in_array( $use_bootstrap, [ 'true', 'yes' ], true ) ); + } + + public function start() { + $this->maybe_create_db(); + } + + /** + * Helper function to auto create database if not exist avoiding having to manually create each db for multiple project development + */ + public function maybe_create_db() { + $dbh = mysqli_init(); + mysqli_real_connect( $dbh, DB_HOST, DB_USER, DB_PASSWORD ); + + if ( ! $dbh ) { + return; + } + + $sql = sprintf( 'create database if not exists %s;', DB_NAME ); + mysqli_query( $dbh, $sql ); + mysqli_select_db( $dbh, DB_NAME ); + + } + + /** + * Responsible to detect and setup various WP constant for VIP Go vs VIP Classic configuration + */ + public function define_constants() { + $site = $this->detect_site(); + + if ( $site ) { + if ( defined( 'IS_UNIT_TEST' ) && IS_UNIT_TEST ) { + unset( $site->db_name ); + unset( $site->hosts ); + } + + if ( ! empty( $site->hosts ) ) { + if ( empty( $_SERVER['HTTP_HOST'] ) || ! in_array( $_SERVER['HTTP_HOST'], $site->hosts ) ) { + $_SERVER['HTTP_HOST'] = $_SERVER['SERVER_NAME'] = $site->hosts[0]; + } + } + + if ( ! empty( $site->db_name ) ) { + ifdefenv( 'DB_NAME', $site->db_name ); + } + + if ( ! empty( $site->name ) ) { + ifndef( 'SITE_NAME', $site->name ); + } + + if ( ! empty( $site->hosting_env ) ) { + switch( $site->hosting_env ) { + case "vipclassic": + ifdefenv('IS_VIP_GO', false); + break; + case "vipgo": + ifdefenv('IS_VIP_GO', true); + break; + default: + break; + } + } + + if ( ! empty( $_SERVER['HTTP_HOST'] ) ) { + ifndef( 'WP_HOME', 'https://'. $_SERVER['HTTP_HOST'] ); + ifndef( 'WP_SITEURL', WP_HOME ); + } + + } + + if ( defined( 'DB_NAME' ) ) { + ifndef( 'AUTH_KEY', DB_NAME ); + ifndef( 'AUTH_SALT', DB_NAME ); + ifndef( 'NONCE_KEY', DB_NAME ); + ifndef( 'NONCE_SALT', DB_NAME ); + ifndef( 'LOGGED_IN_KEY', DB_NAME ); + ifndef( 'LOGGED_IN_SALT', DB_NAME ); + ifndef( 'SECURE_AUTH_KEY', DB_NAME ); + ifndef( 'SECURE_AUTH_SALT', DB_NAME ); + } + + ifndef( 'IS_VIP_GO', true ); + if ( IS_VIP_GO ) { + ifndef( 'VIP_GO_ENV', 'dev' ); + ifndef( 'PMC_IS_VIP_GO_SITE', true ); + } + + } + + /** + * Auto detect current site base on available wp themes information + * @return array|bool|mixed|object + */ + public function detect_site() { + $this->detect_wp_themes(); + $cwd = getcwd(); + if ( isset( $_SERVER['HTTP_HOST'] ) ) { + foreach ( $this->_wp_themes_info as $item ) { + if ( in_array( $_SERVER['HTTP_HOST'], $item->hosts ) ) { + $this->site_info = $item; + break; + } + if ( $cwd === $item->folder ) { + $this->site_info = $item; + break; + } + } + } else { + $this->site_info = $this->get_folder_info( $cwd ); + } + return $this->site_info; + } + + /** + * Auto detect all available themes + */ + public function detect_wp_themes() { + // @TODO: + } + + /** + * Helper function to load and detect theme folder for related information + * @param $folder + * @return array|mixed|object + */ + public function get_folder_info( $folder ) { + + if ( file_exists( $folder . '/.pmc-dev.json' ) ) { + $settings = json_decode( file_get_contents( $folder . '/.pmc-dev.json' ), false ); + if ( empty( $settings ) ) { + $settings = (object) []; + } + } else { + $settings = (object) []; + } + + if ( preg_match( '@/pmc-plugins@', $folder ) ) { + $settings = (object)[ + 'hosting_env' => 'vipclassic', + 'hosts' => [ 'pmc-plugins.' . $this->_domain ], + 'name' => 'pmc-plugins', + 'theme' => false, + ]; + } + + $settings->folder = $folder; + if ( ! isset( $settings->theme ) ) { + $settings->theme = basename( $folder ); + if ( preg_match( '#(?:vip/)?[^/]+$#', $folder, $matches ) ) { + $settings->theme = $matches[0]; + } + } + + if ( ! isset( $settings->name ) ) { + $name = basename( $folder ); + $name = str_replace( 'pmc-', '', $name ); + $name = preg_replace( '/-\d+.*/', '', $name ); + $settings->name = $name; + } + + if ( empty( $settings->hosts ) ) { + $settings->hosts = [ + $settings->name . '.' . $this->_domain, + basename( $settings->theme ) . '.' . $this->_domain, + ]; + } elseif ( ! in_array( basename( $settings->theme ) . '.' . $this->_domain, $settings->hosts ) ) { + $settings->hosts[] = basename( $settings->theme ) . '.' . $this->_domain; + } + + if ( empty( $settings->hosting_env ) ) { + if ( file_exists( $settings->folder . '/docker-compose.yml' ) ) { + $bufs = file_get_contents( $settings->folder . '/docker-compose.yml' ); + if ( strpos( $bufs, 'vipclassic' ) ) { + $settings->hosting_env = 'vipclassic'; + } elseif ( strpos( $bufs, 'vipgo' ) ) { + $settings->hosting_env = 'vipgo'; + } + } + if ( empty( $settings->hosting_env ) ) { + if ( 'vip/' === substr( $settings->theme, 4 ) || preg_match( '#^.*/wpcom/#', $settings->folder, $matches ) ) { + $settings->hosting_env = 'vipclassic'; + } elseif ( preg_match( '#^.*/vipgo/#', $settings->folder, $matches ) ) { + $settings->hosting_env = 'vipgo'; + } else { + $settings->hosting_env = false; + } + } + } + + return $settings; + + } + +} + +Bootstrap::get_instance(); diff --git a/src/wp-core/wp-config/functions.php b/src/wp-core/wp-config/functions.php new file mode 100644 index 0000000..1780286 --- /dev/null +++ b/src/wp-core/wp-config/functions.php @@ -0,0 +1,30 @@ +