From ebf1b2c728e38fde1118783b0c1490bc067e05a7 Mon Sep 17 00:00:00 2001 From: owenbush <ojb@ukhhf.co.uk> Date: Sat, 26 Dec 2020 15:18:52 -0700 Subject: [PATCH] Add circle CI config --- .circleci/build.sh | 124 ++++++++++++++++++++++++++++++++ .circleci/config.yml | 127 +++++++++++++++++++++++++++++++++ .circleci/deploy.sh | 79 ++++++++++++++++++++ .circleci/lint.sh | 25 +++++++ .circleci/process-artifacts.sh | 13 ++++ .circleci/test.sh | 53 ++++++++++++++ 6 files changed, 421 insertions(+) create mode 100755 .circleci/build.sh create mode 100644 .circleci/config.yml create mode 100755 .circleci/deploy.sh create mode 100755 .circleci/lint.sh create mode 100755 .circleci/process-artifacts.sh create mode 100755 .circleci/test.sh diff --git a/.circleci/build.sh b/.circleci/build.sh new file mode 100755 index 00000000..884d15b0 --- /dev/null +++ b/.circleci/build.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash +## +# Build Drupal site using SQLite database, install current module and serve +# using in-built PHP server. +# +# Allows to use the latest Drupal core as well as specified versions (for +# testing backward compatibility). +# +# - builds Drupal site codebase with current module and it's dependencies. +# - installs Drupal using SQLite database. +# - starts in-built PHP-server +# - enables module +# - serves site +# - generates one-time login link +# +# This script will re-build everything from scratch every time it runs. + +# shellcheck disable=SC2015,SC2094 + +set -e + +#------------------------------------------------------------------------------- +# Variables (passed from environment; provided for reference only). +#------------------------------------------------------------------------------- + +# Directory where Drupal site will be built. +BUILD_DIR="${BUILD_DIR:-build}" + +# Webserver hostname. +WEBSERVER_HOST="${WEBSERVER_HOST:-localhost}" + +# Webserver port. +WEBSERVER_PORT="${WEBSERVER_PORT:-8000}" + +# Drupal core version to use. If not provided - latest version will be used. +# Must be coupled with DRUPAL_PROJECT_SHA below. +DRUPAL_VERSION="${DRUPAL_VERSION:-}" + +# Commit SHA of the drupal-project to install custom core version. If not +# provided - the latest version will be used. +# Must be coupled with DRUPAL_VERSION above. +DRUPAL_PROJECT_SHA="${DRUPAL_PROJECT_SHA:-}" + +# Drupal profile to use when installing site. +DRUPAL_PROFILE="${DRUPAL_PROFILE:-standard}" + +# Module name, taken from .info file. +MODULE="$(basename -s .info.yml -- ./*.info.yml)" + +# Database file path. +DB_FILE="${DB_FILE:-/tmp/site_${MODULE}.sqlite}" + +#------------------------------------------------------------------------------- + +echo "==> Validate requirements." +! command -v git > /dev/null && echo "ERROR: Git is required for this script to run." && exit 1 +! command -v php > /dev/null && echo "ERROR: PHP is required for this script to run." && exit 1 +! command -v composer > /dev/null && echo "ERROR: Composer (https://getcomposer.org/) is required for this script to run." && exit 1 +! command -v jq > /dev/null && echo "ERROR: jq (https://stedolan.github.io/jq/) is required for this script to run." && exit 1 + +echo "==> Validate Composer config." +composer validate --ansi --strict + +[ -d "${BUILD_DIR}" ] && echo "==> Remove existing ${BUILD_DIR} directory." && chmod -Rf 777 "${BUILD_DIR}" && rm -rf "${BUILD_DIR}" + +# Allow installing custom version of Drupal core, but only coupled with +# drupal-project SHA (required to get correct dependencies). +if [ -n "${DRUPAL_VERSION}" ] && [ -n "${DRUPAL_PROJECT_SHA}" ]; then + echo "==> Initialise Drupal site from the scaffold commit ${DRUPAL_PROJECT_SHA}." + + git clone -n https://github.com/drupal-composer/drupal-project.git "${BUILD_DIR}" + git --git-dir="${BUILD_DIR}/.git" --work-tree="${BUILD_DIR}" checkout "${DRUPAL_PROJECT_SHA}" + rm -rf "${BUILD_DIR}/.git" > /dev/null + + echo "==> Pin Drupal to a specific version ${DRUPAL_VERSION}." + sed_opts=(-i) && [ "$(uname)" == "Darwin" ] && sed_opts=(-i '') + sed "${sed_opts[@]}" 's|\(.*"drupal\/core"\): "\(.*\)",.*|\1: '"\"$DRUPAL_VERSION\",|" "${BUILD_DIR}/composer.json" + cat "${BUILD_DIR}/composer.json" + + echo "==> Install dependencies." + php -d memory_limit=-1 "$(command -v composer)" --working-dir="${BUILD_DIR}" install +else + echo "==> Initialise Drupal site from the latest scaffold." + php -d memory_limit=-1 "$(command -v composer)" create-project drupal-composer/drupal-project:8.x-dev "${BUILD_DIR}" --no-interaction +fi + +echo "==> Install additional dev dependencies from module's composer.json." +cat <<< "$(jq --indent 4 -M -s '.[0] * .[1]' composer.json "${BUILD_DIR}/composer.json")" > "${BUILD_DIR}/composer.json" +php -d memory_limit=-1 "$(command -v composer)" --working-dir="${BUILD_DIR}" update --lock + +echo "==> Install other dev dependencies." +cat <<< "$(jq --indent 4 '.extra["phpcodesniffer-search-depth"] = 10' "${BUILD_DIR}/composer.json")" > "${BUILD_DIR}/composer.json" +php -d memory_limit=-1 "$(command -v composer)" --working-dir="${BUILD_DIR}" require --dev dealerdirect/phpcodesniffer-composer-installer + +echo "==> Start inbuilt PHP server at http://${WEBSERVER_HOST}:${WEBSERVER_PORT} in $(pwd)/${BUILD_DIR}/web." +killall -9 php > /dev/null 2>&1 || true +nohup php -S "${WEBSERVER_HOST}:${WEBSERVER_PORT}" -t "$(pwd)/${BUILD_DIR}/web" "$(pwd)/${BUILD_DIR}/web/.ht.router.php" > /tmp/php.log 2>&1 & +sleep 4 # Waiting for the server to be ready. +netstat_opts='-tulpn'; [ "$(uname)" == "Darwin" ] && netstat_opts='-anv' || true; +netstat "${netstat_opts[@]}" | grep -q "${WEBSERVER_PORT}" || (echo "ERROR: Unable to start inbuilt PHP server" && cat /tmp/php.log && exit 1) +curl -s -o /dev/null -w "%{http_code}" -L -I "http://${WEBSERVER_HOST}:${WEBSERVER_PORT}" | grep -q 200 || (echo "ERROR: Server is started, but site cannot be served" && exit 1) + +echo "==> Install Drupal into SQLite database ${DB_FILE}." +"${BUILD_DIR}/vendor/bin/drush" -r "${BUILD_DIR}/web" si "${DRUPAL_PROFILE}" -y --db-url "sqlite://${DB_FILE}" --account-name=admin install_configure_form.enable_update_status_module=NULL install_configure_form.enable_update_status_emails=NULL +"${BUILD_DIR}/vendor/bin/drush" -r "$(pwd)/${BUILD_DIR}/web" status + +echo "==> Symlink module code." +rm -rf "${BUILD_DIR}/web/modules/${MODULE}"/* > /dev/null +mkdir -p "${BUILD_DIR}/web/modules/${MODULE}" +ln -s "$(pwd)"/* "${BUILD_DIR}/web/modules/${MODULE}" && rm "${BUILD_DIR}/web/modules/${MODULE}/${BUILD_DIR}" + +echo "==> Enable module ${MODULE}." +"${BUILD_DIR}/vendor/bin/drush" -r "${BUILD_DIR}/web" pm:enable "${MODULE}" -y +"${BUILD_DIR}/vendor/bin/drush" -r "${BUILD_DIR}/web" cr + +# Visit site to pre-warm caches. +curl -s "http://${WEBSERVER_HOST}:${WEBSERVER_PORT}" > /dev/null + +echo -n "==> One-time login link: " +"${BUILD_DIR}/vendor/bin/drush" -r "${BUILD_DIR}/web" -l "http://${WEBSERVER_HOST}:${WEBSERVER_PORT}" uli --no-browser + +echo +echo "==> Build finished. The site is available at http://${WEBSERVER_HOST}:${WEBSERVER_PORT}." +echo diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..eccb0601 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,127 @@ +# @see https://github.com/integratedexperts/drupal_circleci +version: 2 +aliases: + # SSH deployment key fingerprint from CircleCI App -> Project -> Settings -> SSH Permissions. + # Replace the value for your project. + - &deploy_ssh_fingerprint "2d:71:4d:aa:4d:34:38:b5:8f:af:ca:3b:d4:82:6a:21" + - &container_config + working_directory: ~/project + docker: + - image: circleci/php:7.4-cli-browsers + +job-build: &job-build + steps: + - checkout + - run: | + sudo -E apt-get update && sudo -E apt-get install -y libfreetype6-dev libjpeg62-turbo-dev libpng-dev jq \ + && sudo -E docker-php-ext-install -j$(nproc) iconv \ + && if [ "$(php -r "echo PHP_MAJOR_VERSION;")" -gt 5 ] && [ "$(php -r "echo PHP_MINOR_VERSION;")" -gt 3 ] ; then sudo -E docker-php-ext-configure gd --with-freetype --with-jpeg; else sudo -E docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/; fi \ + && sudo -E docker-php-ext-install -j$(nproc) gd + - run: .circleci/build.sh + - run: .circleci/lint.sh + - run: .circleci/test.sh + - run: + command: .circleci/process-artifacts.sh + when: always + - store_test_results: + path: /tmp/test_results + when: always + - store_artifacts: + path: /tmp/artifacts + when: always + +jobs: + build-php-7.4: + <<: *container_config + <<: *job-build + + build-php-7.3: + <<: *container_config + docker: + - image: circleci/php:7.3-cli-browsers + <<: *job-build + + build-php-7.2: + <<: *container_config + docker: + - image: circleci/php:7.2-cli-browsers + <<: *job-build + + build-php-7.4-legacy: + <<: *container_config + environment: + DRUPAL_VERSION: 8.8.10 + DRUPAL_PROJECT_SHA: 8.x + <<: *job-build + + build-php-7.3-legacy: + <<: *container_config + docker: + - image: circleci/php:7.3-cli-browsers + environment: + DRUPAL_VERSION: 8.8.10 + DRUPAL_PROJECT_SHA: 8.x + <<: *job-build + + build-php-7.2-legacy: + <<: *container_config + docker: + - image: circleci/php:7.2-cli-browsers + environment: + DRUPAL_VERSION: 8.8.10 + DRUPAL_PROJECT_SHA: 8.x + <<: *job-build + + deploy: + <<: *container_config + environment: + DEPLOY_SSH_FINGERPRINT: *deploy_ssh_fingerprint + steps: + - checkout + - add_ssh_keys: + fingerprints: + - *deploy_ssh_fingerprint + - run: DEPLOY_BRANCH=${CIRCLE_BRANCH} .circleci/deploy.sh + +workflows: + version: 2 + main: + jobs: + - build-php-7.4: + filters: + tags: + only: /.*/ + - build-php-7.3: + filters: + tags: + only: /.*/ + - build-php-7.2: + filters: + tags: + only: /.*/ + - build-php-7.4-legacy: + filters: + tags: + only: /.*/ + - build-php-7.3-legacy: + filters: + tags: + only: /.*/ + - build-php-7.2-legacy: + filters: + tags: + only: /.*/ + - deploy: + requires: + - build-php-7.4 + - build-php-7.3 + - build-php-7.2 + - build-php-7.4-legacy + - build-php-7.3-legacy + - build-php-7.2-legacy + filters: + tags: + only: /.*/ + branches: + # 7.x, 8.x, 7.x-1.x, 8.x-1.x, 7.x-2.x, 8.x-2.x, ci + only: /^(?:7|8)\.x(?:\-[0-9]+\.x)?|ci$/ diff --git a/.circleci/deploy.sh b/.circleci/deploy.sh new file mode 100755 index 00000000..fbbeae49 --- /dev/null +++ b/.circleci/deploy.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +## +# Deploy code to a remote repository. +# +# - configures local git +# - adds deployment SSH key to SSH agent +# - force-pushes code to a remote code repository branch +# +# It is a good practice to create a separate Deployer user with own SSH key for +# every project. +# +# Add the following variables through CircleCI UI. +# - DEPLOY_USER_NAME - name of the user who will be committing to a remote repository. +# - DEPLOY_USER_EMAIL - email address of the user who will be committing to a remote repository. +# - DEPLOY_REMOTE - remote repository to push code to. +# - DEPLOY_PROCEED - set to 1 if the deployment should proceed. Useful for testing CI configuration before an actual code push. + +set -e + +#------------------------------------------------------------------------------- +# Variables (passed from environment; provided for reference only). +#------------------------------------------------------------------------------- + +# Name of the user who will be committing to a remote repository. +DEPLOY_USER_NAME="${DEPLOY_USER_NAME}" + +# Email address of the user who will be committing to a remote repository. +DEPLOY_USER_EMAIL="${DEPLOY_USER_EMAIL}" + +# Remote repository to push code to. +DEPLOY_REMOTE="${DEPLOY_REMOTE:-}" + +# Git branch to deploy. If not provided - current branch will be used. +DEPLOY_BRANCH="${DEPLOY_BRANCH:-}" + +# The fingerprint of the SSH key of the user on behalf of which the deployment +# is performed. +DEPLOY_SSH_FINGERPRINT="${DEPLOY_SSH_FINGERPRINT:-}" + +# Set to 1 if the deployment should proceed. Useful for testing CI configuration +# before an actual code push. +DEPLOY_PROCEED="${DEPLOY_PROCEED:-0}" + +#------------------------------------------------------------------------------- + +[ -z "${DEPLOY_USER_NAME}" ] && echo "ERROR: Missing required value for DEPLOY_USER_NAME" && exit 1 +[ -z "${DEPLOY_USER_EMAIL}" ] && echo "ERROR: Missing required value for DEPLOY_USER_EMAIL" && exit 1 +[ -z "${DEPLOY_REMOTE}" ] && echo "ERROR: Missing required value for DEPLOY_REMOTE" && exit 1 +[ -z "${DEPLOY_SSH_FINGERPRINT}" ] && echo "ERROR: Missing required value for DEPLOY_SSH_FINGERPRINT" && exit 1 + +[ "${DEPLOY_PROCEED}" != "1" ] && echo "==> Skipping deployment because \$DEPLOY_PROCEED is not set to 1" && exit 0 + +# Configure git and SSH to connect to remote servers for deployment. +mkdir -p "${HOME}/.ssh/" +echo -e "Host *\n\tStrictHostKeyChecking no\n" > "${HOME}/.ssh/config" +DEPLOY_SSH_FILE="${DEPLOY_SSH_FINGERPRINT//:}" +DEPLOY_SSH_FILE="${HOME}/.ssh/id_rsa_${DEPLOY_SSH_FILE//\"}" +[ ! -f "${DEPLOY_SSH_FILE}" ] && echo "ERROR: Unable to find Deploy SSH key file ${DEPLOY_SSH_FILE}." && exit 1 +if [ -z "${SSH_AGENT_PID}" ]; then eval "$(ssh-agent)"; fi +ssh-add -D > /dev/null +ssh-add "${DEPLOY_SSH_FILE}" + +# Configure git user name and email, but only if not already set. +[ "$(git config --global user.name)" == "" ] && echo "==> Configuring global git user name ${DEPLOY_USER_NAME}." && git config --global user.name "${DEPLOY_USER_NAME}" +[ "$(git config --global user.email)" == "" ] && echo "==> Configuring global git user email ${DEPLOY_USER_EMAIL}." && git config --global user.email "${DEPLOY_USER_EMAIL}" + +# Set git to push to a matching remote branch. +git config --global push.default matching + +echo "==> Adding remote ${DEPLOY_REMOTE}." +git remote add deployremote "${DEPLOY_REMOTE}" + +echo "==> Deploying to remote ${DEPLOY_REMOTE}." +# shellcheck disable=SC2086 +git push --force --tags deployremote ${DEPLOY_BRANCH} + +echo +echo "==> Deployment finished." +echo diff --git a/.circleci/lint.sh b/.circleci/lint.sh new file mode 100755 index 00000000..edc89d07 --- /dev/null +++ b/.circleci/lint.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +## +# Run lint checks. +# + +set -e + +#------------------------------------------------------------------------------- +# Variables (passed from environment; provided for reference only). +#------------------------------------------------------------------------------- + +# Directory where Drupal site will be built. +BUILD_DIR="${BUILD_DIR:-build}" + +# Module name, taken from .info file. +MODULE="$(basename -s .info.yml -- ./*.info.yml)" + +#------------------------------------------------------------------------------- + +echo "==> Lint code." +build/vendor/bin/phpcs \ + -s \ + --standard=Drupal,DrupalPractice \ + --extensions=module,php,install,inc,test,info.yml,js \ + "build/web/modules/${MODULE}" diff --git a/.circleci/process-artifacts.sh b/.circleci/process-artifacts.sh new file mode 100755 index 00000000..ba84e002 --- /dev/null +++ b/.circleci/process-artifacts.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash +## +# Process test artifacts. +# +# This runs only in CircleCI. +# +set -e + +if [ -d "$(pwd)/build/web/sites/simpletest/browser_output" ]; then + echo "==> Copying Simpletest test artifacts" + mkdir -p /tmp/artifacts/simpletest + cp -Rf "$(pwd)/build/web/sites/simpletest/browser_output/." /tmp/artifacts/simpletest +fi diff --git a/.circleci/test.sh b/.circleci/test.sh new file mode 100755 index 00000000..c17c8c3c --- /dev/null +++ b/.circleci/test.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +## +# Run tests. +# + +set -e + +#------------------------------------------------------------------------------- +# Variables (passed from environment; provided for reference only). +#------------------------------------------------------------------------------- + +# Directory where Drupal site will be built. +BUILD_DIR="${BUILD_DIR:-build}" + +# Webserver hostname. +WEBSERVER_HOST="${WEBSERVER_HOST:-localhost}" + +# Webserver port. +WEBSERVER_PORT="${WEBSERVER_PORT:-8000}" + +# Module name, taken from .info file. +MODULE="$(basename -s .info.yml -- ./*.info.yml)" + +# Test database file path. +TEST_DB_FILE="${TEST_DB_FILE:-/tmp/test.sqlite}" + +# Directory to store test results. +TEST_RESULTS_DIR="${TEST_RESULTS_DIR:-/tmp/test_results/simpletest}" + +#------------------------------------------------------------------------------- + +echo "==> Run tests." + +# Do not fail if there are no tests. +[ ! -d "tests" ] && echo "==> No tests were found. Skipping." && exit 0 + +# Re-create test results directory. +rm -rf "${TEST_RESULTS_DIR}" > /dev/null +mkdir -p "${TEST_RESULTS_DIR}" + +# Remove existing test DB file. +rm -f "${TEST_DB_FILE}" > /dev/null + +php "./${BUILD_DIR}/web/core/scripts/run-tests.sh" \ + --sqlite "${TEST_DB_FILE}" \ + --dburl "sqlite://localhost/${TEST_DB_FILE}" \ + --url "http://${WEBSERVER_HOST}:${WEBSERVER_PORT}" \ + --non-html \ + --xml "${TEST_RESULTS_DIR}" \ + --color \ + --verbose \ + --suppress-deprecations \ + --module "${MODULE}" -- GitLab