diff --git a/.ahoy.yml b/.ahoy.yml deleted file mode 100644 index bbc5428dd..000000000 --- a/.ahoy.yml +++ /dev/null @@ -1,203 +0,0 @@ ---- -ahoyapi: v2 - -commands: - build: - usage: Build or rebuild project. - cmd: | - ahoy clean \ - && if [ "$COMPOSER" != "composer.json" ]; then ahoy composer-merge; fi \ - && if [ "$COMPOSER" != "composer.json" ]; then ahoy link-package; fi \ - && (docker network prune -f > /dev/null && docker network inspect amazeeio-network > /dev/null || docker network create amazeeio-network) \ - && ahoy up -- --build --force-recreate \ - && ahoy install-dev \ - && if [ "$COMPOSER" != "composer.json" ] && [ "$DRUPAL_PROFILE" ]; then ahoy init-profile; fi \ - && ahoy install-site \ - && if [ "$COMPOSER" != "composer.json" ]; then ahoy init-module; fi \ - && if [ "$COMPOSER" != "composer.json" ]; then ahoy drush pm-enable tide_test -y; fi \ - && ahoy line "Build complete" \ - && ahoy info 1 - - info: - usage: Print information about this project. - cmd: | - ahoy line "Site URL : " ${LOCALDEV_URL}:${LOCAL_PORT:-80}/ - ahoy line "Path to project : " ${APP} - ahoy line "Path to docroot : " ${APP}/${WEBROOT} - ahoy line "DB port on host : " $(docker port $(docker-compose ps -q mariadb) 3306 | cut -d : -f 2) - ahoy line "Mailhog URL : " http://mailhog.docker.internal:${LOCAL_PORT:-80}/ - if [ "$1" ]; then - ahoy line "One-time login : " $(ahoy login -- --no-browser) - fi - - up: - usage: Build and start Docker containers. - cmd: | - docker-compose up -d "$@" \ - && ahoy cli "dockerize -wait tcp://mariadb:3306 -timeout 1m" \ - && if docker-compose logs | grep -q "\[Error\]"; then docker-compose logs; exit 1; fi \ - && docker ps -a --filter name=^/${COMPOSE_PROJECT_NAME}_ && - # Add Bay SSH key before fetching DB. - # During local development, pygmy injects SSH key from the host, so specifying - # key as $BAY_KEY environment variable is not required (the host key of every - # developer must be added to Bay during project onboarding). - if [ "$BAY_KEY" ]; then - ahoy cli "mkdir -p /home/.ssh && echo -e \"$BAY_KEY\" > /home/.ssh/key && chmod 600 /home/.ssh/key;" - fi - - down: - usage: Stop Docker containers and remove container, images, volumes and networks. - cmd: docker-compose down --volumes - - start: - usage: Start existing Docker containers. - cmd: docker-compose start "$@" - - stop: - usage: Stop running Docker containers. - cmd: docker-compose stop "$@" - - restart: - usage: Restart all stopped and running Docker containers. - cmd: docker-compose restart - - logs: - usage: Show Docker logs. - cmd: docker-compose logs "$@" - - pull: - usage: Pull latest docker images. - cmd: | - docker image ls --format \"{{.Repository}}:{{.Tag}}\" | grep singledigital/ | grep -v none | xargs -n1 docker pull | cat \ - && docker image ls --format \"{{.Repository}}:{{.Tag}}\" | grep amazeeio/ | grep -v none | xargs -n1 docker pull | cat - - cli: - usage: Start a shell inside CLI container or run a command. - cmd: if \[ "$@" \]; then docker exec -i $(docker-compose ps -q cli) bash -c "$@"; else docker exec -it $(docker-compose ps -q cli) bash; fi - - drush: - usage: Run drush commands in the CLI service container. - cmd: docker exec -i $(docker-compose ps -q cli) drush -r ${APP}/${WEBROOT} -l ${LOCALDEV_URL} "$@" - - login: - usage: Login to a website. - cmd: ahoy drush user:unblock -q "$(ahoy drush user:information --uid=1 --fields=name --format=string)" 2>/dev/null && ahoy drush user:login --no-browser - - doctor: - usage: Find problems with current project setup. - cmd: scripts/doctor.sh - - # @note: Call this on the host before starting containers. - composer-merge: - usage: Merge composer files. - cmd: scripts/composer-merge.sh - - install-dev: - usage: Install dependencies. - cmd: ahoy cli "composer install -n --ansi --prefer-dist --no-suggest" - - install-site: - usage: Install site. - cmd: | - ahoy flush-redis - if [ "$INSTALL_NEW_SITE" == "1" ]; then - ahoy cli -- ./scripts/drupal/install-new-site.sh - else - ahoy cli -- ./scripts/rebuild-env.sh - fi - - db-import: - usage: Import the production database. - cmd: ahoy cli -- ./scripts/rebuild-env.sh - - fix-bay-token: - usage: Fix the bay token by re-importing. - cmd: | - pygmy stop \ - && sleep 1 \ - && pygmy start \ - && docker ps -a | grep Exit | cut -d ' ' -f 1 | xargs docker rm \ - && docker-compose up -d --force \ - && pygmy stop \ - && sleep 1 \ - && pygmy start - - link-package: - usage: Link package from current repository root. - cmd: scripts/link-package.sh - hide: true - - init-module: - usage: Install a module from current repository root. - cmd: ahoy cli "INSTALL_SUGGEST=$INSTALL_SUGGEST scripts/drupal-init-module.sh" - hide: true - - init-profile: - usage: Install a profile from current repository root. - cmd: ahoy cli "INSTALL_SUGGEST=$INSTALL_SUGGEST scripts/drupal-init-profile.sh" - hide: true - - clean: - usage: Remove all build files. - cmd: | - ahoy down \ - && ([ -d .git ] && git ls-files --directory --other -i --exclude-from=.gitignore ${WEBROOT} | grep -v "settings.local.php" | grep -v "services.local.yml" | xargs chmod -Rf 777 || true) \ - && ([ -d .git ] && git ls-files --directory --other -i --exclude-from=.gitignore ${WEBROOT} | grep -v "settings.local.php" | grep -v "services.local.yml" | xargs rm -Rf || true) \ - && rm -Rf vendor \ - && rm -Rf screenshots \ - && rm -Rf composer.build.* - - clean-full: - usage: Remove all development files. - cmd: | - ahoy clean \ - && ([ -d .git ] && git ls-files --directory --other . | grep -v ".idea" | xargs chmod -Rf 777 || true) \ - && ([ -d .git ] && git ls-files --directory --other . | grep -v ".idea" | xargs rm -Rf || true) - - deploy: - usage: Deploy or re-deploy a branch in Bay. - cmd: ./scripts/bay-deploy.sh "$@" - - lint: - usage: Lint code. - cmd: ahoy cli "vendor/bin/phpcs ${PHPCS_TARGETS}" - - lint-fix: - usage: Fix lint errors. - cmd: ahoy cli "vendor/bin/phpcbf ${PHPCS_TARGETS}" - - test-behat: - usage: Run Behat tests. - cmd: ahoy cli "./vendor/bin/behat --strict --colors ${BEHAT_PROFILE} $@" - - flush-redis: - usage: Flush Redis cache. - cmd: docker exec -i $(docker-compose ps -q redis) redis-cli flushall > /dev/null - - # Utilities. - line: - cmd: printf "$(tput -Txterm setaf 2)${1}$(tput -Txterm sgr0)${2}\n" - hide: true - -entrypoint: - - bash - - "-c" - - | - [ -f .env ] && export $(grep -v '^#' .env | xargs) && [ -f .env.local ] && export $(grep -v '^#' .env.local | xargs) - export DOCKER_BUILDKIT=0 - export COMPOSE_DOCKER_CLI_BUILD=0 - export PROJECT_NAME=${PROJECT_NAME:-$(basename $(pwd))} - export APP=${APP:-/app} - export WEBROOT=${WEBROOT:-docroot} - export MYSQL_HOST=${MYSQL_HOST:-mariadb} - export MYSQL_PORT=${MYSQL_PORT:-3306} - export COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME:-$PROJECT_NAME} - export LOCALDEV_URL=${LOCALDEV_URL:-http://content-sdp.docker.internal} - export COMPOSER=${COMPOSER:-composer.json} - export DRUPAL_PROFILE=${DRUPAL_PROFILE:-} - export DRUPAL_MODULE_PREFIX=${DRUPAL_MODULE_PREFIX:-mysite} - PHPCS_TARGETS=${PHPCS_TARGETS:-.} && export PHPCS_TARGETS=${PHPCS_TARGETS//,/ } - if [ "$CI" ]; then export ES_TPL=elasticsearch.ci.yml; fi - bash -c "$0" "$@" - - '{{cmd}}' - - '{{name}}' diff --git a/.ddev/addon-metadata/ddev-drupal-contrib/manifest.yaml b/.ddev/addon-metadata/ddev-drupal-contrib/manifest.yaml new file mode 100644 index 000000000..864be2588 --- /dev/null +++ b/.ddev/addon-metadata/ddev-drupal-contrib/manifest.yaml @@ -0,0 +1,19 @@ +name: ddev-drupal-contrib +repository: ddev/ddev-drupal-contrib +version: 1.1.5 +install_date: "2026-07-04T17:00:13+10:00" +project_files: + - commands/host/core-version + - commands/web/eslint + - commands/web/expand-composer-json + - commands/web/nightwatch + - commands/web/phpcbf + - commands/web/phpcs + - commands/web/phpstan + - commands/web/phpunit + - commands/web/poser + - commands/web/stylelint + - commands/web/symlink-project + - config.contrib.yaml +global_files: [] +removal_actions: [] diff --git a/.ddev/addon-metadata/ddev-selenium-standalone-chrome/manifest.yaml b/.ddev/addon-metadata/ddev-selenium-standalone-chrome/manifest.yaml new file mode 100644 index 000000000..668ca93e3 --- /dev/null +++ b/.ddev/addon-metadata/ddev-selenium-standalone-chrome/manifest.yaml @@ -0,0 +1,19 @@ +name: ddev-selenium-standalone-chrome +repository: ddev/ddev-selenium-standalone-chrome +version: 2.1.0 +install_date: "2026-07-04T17:51:19+10:00" +project_files: + - docker-compose.selenium-chrome.yaml + - config.selenium-standalone-chrome.yaml +global_files: [] +removal_actions: + - | + #ddev-nodisplay + #ddev-description:Remove docker-compose.selenium-chrome_extras.yaml file + if [ -f docker-compose.selenium-chrome_extras.yaml ]; then + if grep -q '#ddev-generated' docker-compose.selenium-chrome_extras.yaml; then + rm -f docker-compose.selenium-chrome_extras.yaml + else + echo "Unwilling to remove '$DDEV_APPROOT/.ddev/docker-compose.selenium-chrome_extras.yaml' because it does not have #ddev-generated in it; you can manually delete it if it is safe to delete." + fi + fi diff --git a/.ddev/addon-metadata/elasticsearch/manifest.yaml b/.ddev/addon-metadata/elasticsearch/manifest.yaml new file mode 100644 index 000000000..878c50958 --- /dev/null +++ b/.ddev/addon-metadata/elasticsearch/manifest.yaml @@ -0,0 +1,9 @@ +name: elasticsearch +repository: ddev/ddev-elasticsearch +version: v0.3.3 +install_date: "2026-07-04T17:40:21+10:00" +project_files: + - elasticsearch/ + - docker-compose.elasticsearch.yaml +global_files: [] +removal_actions: [] diff --git a/.ddev/commands/host/build b/.ddev/commands/host/build new file mode 100755 index 000000000..7ab514706 --- /dev/null +++ b/.ddev/commands/host/build @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +## Description: Full rebuild from scratch: wipe web/ and vendor/, then start, poser, symlink-project and install-site. +## Usage: build +## Example: "ddev build" + +set -eu -o pipefail + +cd "${DDEV_APPROOT}" + +# Drupal locks sites/default read-only after install; unlock it so rm can succeed. +if [ -d web/sites/default ]; then + chmod -R u+w web/sites/default +fi +rm -rf web vendor + +ddev start +ddev poser +ddev symlink-project +ddev install-site diff --git a/.ddev/commands/host/core-version b/.ddev/commands/host/core-version new file mode 100755 index 000000000..e3e012246 --- /dev/null +++ b/.ddev/commands/host/core-version @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +#ddev-generated +## Command provided by https://github.com/ddev/ddev-drupal-contrib +## Description: Switch the core version and rebuild. +## Usage: core-version [version] +## Example: "ddev core-version ^11" or "ddev core-version ~11.1.0" + +set -eu -o pipefail + +# Handle default values. +DRUPAL_CORE=${1:-default} +if [ "$DRUPAL_CORE" == "default" ]; then + DRUPAL_CORE="" +fi + +# Set/clear the env. +ddev dotenv set .ddev/.env.web --drupal-core "${DRUPAL_CORE}" + +# Restart and rebuild. +ddev restart +ddev poser diff --git a/.ddev/commands/web/eslint b/.ddev/commands/web/eslint new file mode 100755 index 000000000..6be295c6a --- /dev/null +++ b/.ddev/commands/web/eslint @@ -0,0 +1,22 @@ +#!/usr/bin/env bash + +#ddev-generated +## Command provided by https://github.com/ddev/ddev-drupal-contrib +## Description: Run eslint inside the web container +## Usage: eslint [flags] [args] +## Example: "ddev eslint" +## ExecRaw: true + +set -eu -o pipefail + +if "$DDEV_DOCROOT/core/node_modules/.bin/eslint" --version >/dev/null ; then + # Configure prettier + test -e .prettierrc.json || ln -s $DDEV_DOCROOT/core/.prettierrc.json . + test -e .prettierignore || echo '*.yml' > .prettierignore + # Change directory to the project root folder + cd "$DDEV_DOCROOT/$DRUPAL_PROJECTS_PATH/${DDEV_SITENAME//-/_}" || exit + "$DDEV_COMPOSER_ROOT/$DDEV_DOCROOT/core/node_modules/.bin/eslint" --config="../../../core/.eslintrc.passing.json" --no-error-on-unmatched-pattern --ignore-pattern="*.es6.js" --resolve-plugins-relative-to=$DDEV_COMPOSER_ROOT/$DDEV_DOCROOT/core --ext=.js,.yml . "$@" +else + echo "eslint is not available. You may need to 'ddev exec \"cd $DDEV_DOCROOT/core && yarn install\"'" + exit 1 +fi diff --git a/.ddev/commands/web/expand-composer-json b/.ddev/commands/web/expand-composer-json new file mode 100755 index 000000000..509e414a9 --- /dev/null +++ b/.ddev/commands/web/expand-composer-json @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +#ddev-generated +## Command provided by https://github.com/ddev/ddev-drupal-contrib +## Description: Add Drupal core and other needed dependencies. +## Usage: expand-composer-json [flags] [PROJECT_NAME] +## Example: "ddev expand-composer-json ctools" +## ExecRaw: true +## MutagenSync: true + +set -eu -o pipefail + +# Set the default core version. +export DRUPAL_CORE=${DRUPAL_CORE:-^11} + +export _WEB_ROOT=$DDEV_DOCROOT +cd "$DDEV_COMPOSER_ROOT" || exit +curl -OL https://git.drupalcode.org/project/gitlab_templates/-/raw/default-ref/scripts/expand_composer_json.php +_ddev_drupal_contrib_empty_composer=false +if [[ ! -f composer.json ]]; then + echo "{}" > composer.json + _ddev_drupal_contrib_empty_composer=true +fi +php expand_composer_json.php "$DDEV_SITENAME" +rm -f expand_composer_json.php +if [ "$_ddev_drupal_contrib_empty_composer" = true ]; then + rm -f composer.json +fi diff --git a/.ddev/commands/web/install-site b/.ddev/commands/web/install-site new file mode 100755 index 000000000..365659383 --- /dev/null +++ b/.ddev/commands/web/install-site @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +## Description: Install a fresh Drupal site (testing profile) and enable tide_core + tide_test. +## Usage: install-site +## Example: "ddev install-site" +## ExecRaw: true + +set -eu -o pipefail + +DRUPAL_PROFILE=${DRUPAL_PROFILE:-testing} +DRUPAL_ADMIN_NAME=${DRUPAL_ADMIN_NAME:-admin} +DRUPAL_ADMIN_PASSWORD=${DRUPAL_ADMIN_PASSWORD:-admin} + +drush site:install "${DRUPAL_PROFILE}" -y \ + --account-name="${DRUPAL_ADMIN_NAME}" \ + --account-pass="${DRUPAL_ADMIN_PASSWORD}" \ + --site-name="Single Digital Presence Content Management System" \ + install_configure_form.enable_update_status_module=NULL \ + install_configure_form.enable_update_status_emails=NULL + +drush user:unblock "${DRUPAL_ADMIN_NAME}" +drush cache:rebuild + +echo "==> Enabling tide_core" +drush pm:enable -y tide_core + +echo "==> Pointing ClamAV at the local ddev service" +drush -y config:set clamav.settings mode_daemon_tcpip.hostname clamav +drush -y config:set clamav.settings mode_daemon_tcpip.port 3310 + +echo "==> Enabling tide_test" +drush pm:enable -y tide_test + +drush cache:rebuild +drush user:login --no-browser diff --git a/.ddev/commands/web/nightwatch b/.ddev/commands/web/nightwatch new file mode 100755 index 000000000..06b2ea261 --- /dev/null +++ b/.ddev/commands/web/nightwatch @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +#ddev-generated +## Command provided by https://github.com/ddev/ddev-drupal-contrib +## Description: Run nightwatch inside the web container +## Usage: nightwatch [flags] [args] +## Example: "ddev nightwatch" +## ExecRaw: true + +set -eu -o pipefail + +yarn --cwd "$DDEV_DOCROOT/core" test:nightwatch "$DDEV_APPROOT/$DDEV_DOCROOT/$DRUPAL_PROJECTS_PATH/" "$@" diff --git a/.ddev/commands/web/phpcbf b/.ddev/commands/web/phpcbf new file mode 100755 index 000000000..25393e07b --- /dev/null +++ b/.ddev/commands/web/phpcbf @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +#ddev-generated +## Command provided by https://github.com/ddev/ddev-drupal-contrib +## Description: Run phpcbf inside the web container +## Usage: phpcbf [flags] [args] +## Example: "ddev phpcbf" or "ddev phpcbf -n" +## ExecRaw: true + +set -eu -o pipefail + +if ! command -v phpcbf >/dev/null; then + echo "phpcbf is not available. You may need to 'ddev composer install'" + exit 1 +fi +test -e phpcs.xml.dist || curl -OL https://git.drupalcode.org/project/gitlab_templates/-/raw/default-ref/assets/phpcs.xml.dist +phpcbf -s --report-full --report-summary --report-source $DDEV_DOCROOT/$DRUPAL_PROJECTS_PATH "$@" diff --git a/.ddev/commands/web/phpcs b/.ddev/commands/web/phpcs new file mode 100755 index 000000000..efcf54af3 --- /dev/null +++ b/.ddev/commands/web/phpcs @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +#ddev-generated +## Command provided by https://github.com/ddev/ddev-drupal-contrib +## Description: Run phpcs inside the web container +## Usage: phpcs [flags] [args] +## Example: "ddev phpcs" or "ddev phpcs -n" +## ExecRaw: true + +set -eu -o pipefail + +if ! command -v phpcs >/dev/null; then + echo "phpcs is not available. You may need to 'ddev composer install'" + exit 1 +fi +test -e phpcs.xml.dist || curl -OL https://git.drupalcode.org/project/gitlab_templates/-/raw/default-ref/assets/phpcs.xml.dist +phpcs -s --report-full --report-summary --report-source $DDEV_DOCROOT/$DRUPAL_PROJECTS_PATH --ignore=*/.ddev/* "$@" diff --git a/.ddev/commands/web/phpstan b/.ddev/commands/web/phpstan new file mode 100755 index 000000000..253ff6d4a --- /dev/null +++ b/.ddev/commands/web/phpstan @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +#ddev-generated +## Command provided by https://github.com/ddev/ddev-drupal-contrib +## Description: Run phpstan inside the web container +## Usage: phpstan [flags] [args] +## Example: "ddev phpstan" or "ddev phpstan -n" +## ExecRaw: true +## MutagenSync: true + +set -eu -o pipefail + +if ! command -v phpstan >/dev/null; then + echo "phpstan is not available. You may need to 'ddev poser'" + exit 1 +fi +test -e phpstan.neon || curl -OL https://git.drupalcode.org/project/gitlab_templates/-/raw/default-ref/assets/phpstan.neon +# See https://git.drupalcode.org/project/gitlab_templates/-/commit/a107b7f1f79af12e0b09f70be47b68e3f69b4504 +sed -i 's/BASELINE_PLACEHOLDER/phpstan-baseline.neon/g' phpstan.neon +# Add an empty baseline file to ensure it exists. +test -e phpstan-baseline.neon || touch phpstan-baseline.neon + +EXTENSION_DIRECTORY=$DDEV_DOCROOT/$DRUPAL_PROJECTS_PATH/${DDEV_SITENAME//-/_} +cd "$EXTENSION_DIRECTORY" || exit 1 +# Ensure PHPStan configuration is symlinked from project root. +ln -s $DDEV_DOCROOT/phpstan.neon 2>/dev/null || true +ln -s $DDEV_DOCROOT/phpstan-baseline.neon 2>/dev/null || true +phpstan analyze . "$@" diff --git a/.ddev/commands/web/phpunit b/.ddev/commands/web/phpunit new file mode 100755 index 000000000..b96730419 --- /dev/null +++ b/.ddev/commands/web/phpunit @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +#ddev-generated +## Command provided by https://github.com/ddev/ddev-drupal-contrib +## Description: Run phpunit inside the web container +## Usage: phpunit [flags] [args] +## Example: "ddev phpunit" or "ddev phpunit --stop-on-failure" +## Example: "ddev phpunit web/modules/custom/mymodule/tests/src/Unit/MyTest.php" +## Example: "ddev phpunit --filter testMyFunction" +## ExecRaw: true + +set -eu -o pipefail + +if ! command -v phpunit >/dev/null; then + echo "phpunit is not available. You may need to 'ddev composer install'" + exit 1 +fi + +# CHECK for local config. +if [ -f "phpunit.xml" ]; then + # Defer to local config + phpunit "$@" +else + # Check if we are running a specific test or test folder. + if [ $# -gt 0 ] && [ -e "$1" ]; then + TEST_PATH="$1" + shift + phpunit --bootstrap $PWD/$DDEV_DOCROOT/core/tests/bootstrap.php "$TEST_PATH" "$@" + else + # Run all custom module tests. + phpunit --bootstrap $PWD/$DDEV_DOCROOT/core/tests/bootstrap.php $DDEV_DOCROOT/$DRUPAL_PROJECTS_PATH "$@" + fi +fi diff --git a/.ddev/commands/web/poser b/.ddev/commands/web/poser new file mode 100755 index 000000000..e6d6617f8 --- /dev/null +++ b/.ddev/commands/web/poser @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +#ddev-generated +## Command provided by https://github.com/ddev/ddev-drupal-contrib +## Description: Expand composer.json and run composer install. +## Usage: poser [flags] [args] +## Example: "ddev poser" or "ddev poser --prefer-source" +## ExecRaw: true +## MutagenSync: true + +set -eu -o pipefail + +export COMPOSER=composer.contrib.json +.ddev/commands/web/expand-composer-json +composer install "$@" +# The -f flag suppresses errors if lock file does not exist. +rm -f composer.contrib.json composer.contrib.lock +touch $DDEV_DOCROOT/core/.env diff --git a/.ddev/commands/web/stylelint b/.ddev/commands/web/stylelint new file mode 100755 index 000000000..521d3bc75 --- /dev/null +++ b/.ddev/commands/web/stylelint @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +#ddev-generated +## Command provided by https://github.com/ddev/ddev-drupal-contrib +## Description: Run stylelint inside the web container +## Usage: stylelint [flags] [args] +## Example: "ddev stylelint" +## ExecRaw: true + +set -eu -o pipefail + +if "$DDEV_DOCROOT/core/node_modules/.bin/stylelint" --version >/dev/null ; then + # Configure prettier + test -e .prettierrc.json || ln -s $DDEV_DOCROOT/core/.prettierrc.json . + # Change directory to the project root folder + cd "$DDEV_DOCROOT/$DRUPAL_PROJECTS_PATH/${DDEV_SITENAME//-/_}" || exit + "$DDEV_COMPOSER_ROOT/$DDEV_DOCROOT/core/node_modules/.bin/stylelint" --color --config "$DDEV_COMPOSER_ROOT/$DDEV_DOCROOT/core/.stylelintrc.json" "./**/*.css" "$@" +else + echo "stylelint is not available. You may need to 'ddev exec \"cd $DDEV_DOCROOT/core && yarn install\"'" + exit 1 +fi diff --git a/.ddev/commands/web/symlink-project b/.ddev/commands/web/symlink-project new file mode 100755 index 000000000..7db02d30c --- /dev/null +++ b/.ddev/commands/web/symlink-project @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +#ddev-generated +## Command provided by https://github.com/ddev/ddev-drupal-contrib +## Description: Symlink project files into the configured location (defaults to `web/modules/custom/[PROJECT_NAME]`) +## Usage: symlink-project [flags] [args] +## Example: "ddev symlink-project" +## ExecRaw: true + +set -eu -o pipefail + +export _WEB_ROOT=$DDEV_DOCROOT +cd "$DDEV_COMPOSER_ROOT" || exit +curl -OL https://git.drupalcode.org/project/gitlab_templates/-/raw/default-ref/scripts/symlink_project.php + +if [ -n "${PROJECT_FILES-}" ]; then + # Suppress a warning. + PROJECT_FILES=$(ls -A --ignore="web" --ignore="vendor" --ignore="symlink_project.php" --ignore=".ddev" --ignore=".idea") + export PROJECT_FILES +fi + +# Symlink name using underscores. +# @see https://www.drupal.org/docs/develop/creating-modules/naming-and-placing-your-drupal-module +# DRUPAL_PROJECT_FOLDER is used by the symlink-project script in gitlab_templates +export DRUPAL_PROJECT_FOLDER=${DDEV_DOCROOT}/${DRUPAL_PROJECTS_PATH}/${DDEV_SITENAME//-/_} +php symlink_project.php +rm -f symlink_project.php diff --git a/.ddev/config.contrib.yaml b/.ddev/config.contrib.yaml new file mode 100644 index 000000000..344b786ae --- /dev/null +++ b/.ddev/config.contrib.yaml @@ -0,0 +1,24 @@ +#ddev-generated +## Command provided by https://github.com/ddev/ddev-drupal-contrib +## To customize this configuration, see: +## https://ddev.readthedocs.io/en/stable/users/extend/customization-extendibility/ +web_environment: + # To change the Drupal core version, see the README: + # https://github.com/ddev/ddev-drupal-contrib/blob/main/README.md#changing-the-drupal-core-version + # https://git.drupalcode.org/project/gitlab_templates/-/blob/1.9.6/scripts/expand_composer_json.php?ref_type=tags#L15 + - IGNORE_PROJECT_DRUPAL_CORE_VERSION=1 + # To change the location of your project code, see the README: + # https://github.com/ddev/ddev-drupal-contrib/blob/main/README.md#changing-the-symlink-location + - DRUPAL_PROJECTS_PATH=modules/custom + - SIMPLETEST_DB=mysql://db:db@db/db + - SIMPLETEST_BASE_URL=http://web + - BROWSERTEST_OUTPUT_DIRECTORY=/tmp + - BROWSERTEST_OUTPUT_BASE_URL=${DDEV_PRIMARY_URL} +hooks: + post-start: + - exec-host: | + if [[ -f vendor/autoload.php ]]; then + ddev symlink-project + else + exit 0 + fi diff --git a/.ddev/config.selenium-standalone-chrome.yaml b/.ddev/config.selenium-standalone-chrome.yaml new file mode 100644 index 000000000..842c650b8 --- /dev/null +++ b/.ddev/config.selenium-standalone-chrome.yaml @@ -0,0 +1,31 @@ +#ddev-generated +# Remove the line above if you don't want this file to be overwritten when you run +# ddev get ddev/ddev-selenium-standalone-chrome +# +# This file comes from https://github.com/ddev/ddev-selenium-standalone-chrome +# +web_environment: + - BROWSERTEST_OUTPUT_DIRECTORY=/tmp + - BROWSERTEST_OUTPUT_BASE_URL=${DDEV_PRIMARY_URL} + - SIMPLETEST_BASE_URL=http://web + - SIMPLETEST_DB=mysql://db:db@db/db + # Use disable-dev-shm-usage instead of setting shm_usage + # https://developers.google.com/web/tools/puppeteer/troubleshooting#tips + # The format of chromeOptions is defined at https://chromedriver.chromium.org/capabilities + - MINK_DRIVER_ARGS_WEBDRIVER=[\"chrome\", {\"browserName\":\"chrome\",\"goog:chromeOptions\":{\"w3c\":true,\"args\":[\"--disable-dev-shm-usage\",\"--disable-gpu\",\"--headless\",\"--dns-prefetch-disable\"]}}, \"http://selenium-chrome:4444/wd/hub\"] + # Nightwatch + - DRUPAL_TEST_BASE_URL=http://web + - DRUPAL_TEST_DB_URL=mysql://db:db@db/db + - DRUPAL_TEST_WEBDRIVER_HOSTNAME=selenium-chrome + - DRUPAL_TEST_WEBDRIVER_PORT=4444 + - DRUPAL_TEST_WEBDRIVER_PATH_PREFIX=/wd/hub + - DRUPAL_TEST_WEBDRIVER_W3C=true + # --window-size=1920,1080 is needed to fix random timeouts in tests. See: https://community.latenode.com/t/selenium-webdriver-timeout-issue-when-running-in-headless-mode-with-c/21952/4 + - DRUPAL_TEST_WEBDRIVER_CHROME_ARGS=--disable-dev-shm-usage --disable-gpu --headless --dns-prefetch-disable --window-size=1920,1080 + - DRUPAL_TEST_CHROMEDRIVER_AUTOSTART=false + - DRUPAL_NIGHTWATCH_SEARCH_DIRECTORY=../ + - DRUPAL_NIGHTWATCH_IGNORE_DIRECTORIES=node_modules,vendor,.*,sites/*/files,sites/*/private,sites/simpletest + - DRUPAL_NIGHTWATCH_OUTPUT=reports/nightwatch + # DTT + - DTT_BASE_URL=http://web + - DTT_MINK_DRIVER_ARGS=[\"chrome\", {\"browserName\":\"chrome\",\"goog:chromeOptions\":{\"w3c\":true,\"args\":[\"--disable-dev-shm-usage\",\"--disable-gpu\",\"--headless\",\"--dns-prefetch-disable\"]}}, \"http://selenium-chrome:4444/wd/hub\"] diff --git a/.ddev/config.yaml b/.ddev/config.yaml new file mode 100644 index 000000000..800a36f97 --- /dev/null +++ b/.ddev/config.yaml @@ -0,0 +1,314 @@ +name: tide-core +type: drupal10 +docroot: web +php_version: "8.3" +webserver_type: nginx-fpm +xdebug_enabled: false +additional_hostnames: [] +additional_fqdns: [] +database: + type: mariadb + version: "10.6" +use_dns_when_possible: true +composer_version: "2" +router_http_port: "8080" +router_https_port: "8443" +corepack_enable: false +web_environment: + # Drupal core version used by `ddev poser` (drupal.org gitlab_templates + # expand_composer_json.php). Keep in sync with drupal/core in composer.json. + - DRUPAL_CORE=10.6.x + +hooks: + post-import-db: + - exec: drush cache:rebuild + - exec: drush updatedb -y + - exec: drush cache:rebuild + +# Key features of DDEV's config.yaml: + +# name: # Name of the project, automatically provides +# http://projectname.ddev.site and https://projectname.ddev.site +# If the name is omitted, the project will take the name of the enclosing directory, +# which is useful if you want to have a copy of the project side by side with this one. + +# type: # backdrop, cakephp, craftcms, drupal, drupal6, drupal7, drupal8, drupal9, drupal10, drupal11, generic, laravel, magento, magento2, php, shopware6, silverstripe, symfony, typo3, wordpress +# See https://ddev.readthedocs.io/en/stable/users/quickstart/ for more +# information on the different project types + +# docroot: # Relative path to the directory containing index.php. + +# php_version: "8.3" # PHP version to use, "5.6" through "8.4" + +# You can explicitly specify the webimage but this +# is not recommended, as the images are often closely tied to DDEV's' behavior, +# so this can break upgrades. + +# webimage: # nginx/php docker image. + +# database: +# type: # mysql, mariadb, postgres +# version: # database version, like "10.11" or "8.0" +# MariaDB versions can be 5.5-10.8, 10.11, and 11.4. +# MySQL versions can be 5.5-8.0. +# PostgreSQL versions can be 9-17. + +# router_http_port: # Port to be used for http (defaults to global configuration, usually 80) +# router_https_port: # Port for https (defaults to global configuration, usually 443) + +# xdebug_enabled: false # Set to true to enable Xdebug and "ddev start" or "ddev restart" +# Note that for most people the commands +# "ddev xdebug" to enable Xdebug and "ddev xdebug off" to disable it work better, +# as leaving Xdebug enabled all the time is a big performance hit. + +# xhgui_https_port: 8142 +# Can be used to change the router https port for xhgui application +# Very rarely used + +# xhgui_http_port: 8143 +# Can be used to change the router http port for xhgui application +# Very rarely used + +# host_xhgui_port: 8142 +# Can be used to change the host binding port of the xhgui +# application. Rarely used; only when port conflict and +# bind_all_ports is used (normally with router disabled) + +# xhprof_enabled: false # Set to true to enable Xhprof and "ddev start" or "ddev restart" +# Note that for most people the commands +# "ddev xhprof" to enable Xhprof and "ddev xhprof off" to disable it work better, +# as leaving Xhprof enabled all the time is a big performance hit. + +# xhprof_mode: [prepend|xhgui|global] +# Set to "xhgui" to enable XHGui features +# "xhgui" will become default in a future major release + +# webserver_type: nginx-fpm, apache-fpm, generic + +# timezone: Europe/Berlin +# If timezone is unset, DDEV will attempt to derive it from the host system timezone +# using the $TZ environment variable or the /etc/localtime symlink. +# This is the timezone used in the containers and by PHP; +# it can be set to any valid timezone, +# see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones +# For example Europe/Dublin or MST7MDT + +# composer_root: +# Relative path to the Composer root directory from the project root. This is +# the directory which contains the composer.json and where all Composer related +# commands are executed. + +# composer_version: "2" +# You can set it to "" or "2" (default) for Composer v2 or "1" for Composer v1 +# to use the latest major version available at the time your container is built. +# It is also possible to use each other Composer version channel. This includes: +# - 2.2 (latest Composer LTS version) +# - stable +# - preview +# - snapshot +# Alternatively, an explicit Composer version may be specified, for example "2.2.18". +# To reinstall Composer after the image was built, run "ddev debug rebuild". + +# nodejs_version: "22" +# change from the default system Node.js version to any other version. +# See https://ddev.readthedocs.io/en/stable/users/configuration/config/#nodejs_version for more information +# and https://www.npmjs.com/package/n#specifying-nodejs-versions for the full documentation, +# Note that using of 'ddev nvm' is discouraged because "nodejs_version" is much easier to use, +# can specify any version, and is more robust than using 'nvm'. + +# corepack_enable: false +# Change to 'true' to 'corepack enable' and gain access to latest versions of yarn/pnpm + +# additional_hostnames: +# - somename +# - someothername +# would provide http and https URLs for "somename.ddev.site" +# and "someothername.ddev.site". + +# additional_fqdns: +# - example.com +# - sub1.example.com +# would provide http and https URLs for "example.com" and "sub1.example.com" +# Please take care with this because it can cause great confusion. + +# upload_dirs: "custom/upload/dir" +# +# upload_dirs: +# - custom/upload/dir +# - ../private +# +# would set the destination paths for ddev import-files to /custom/upload/dir +# When Mutagen is enabled this path is bind-mounted so that all the files +# in the upload_dirs don't have to be synced into Mutagen. + +# disable_upload_dirs_warning: false +# If true, turns off the normal warning that says +# "You have Mutagen enabled and your 'php' project type doesn't have upload_dirs set" + +# ddev_version_constraint: "" +# Example: +# ddev_version_constraint: ">= 1.22.4" +# This will enforce that the running ddev version is within this constraint. +# See https://github.com/Masterminds/semver#checking-version-constraints for +# supported constraint formats + +# working_dir: +# web: /var/www/html +# db: /home +# would set the default working directory for the web and db services. +# These values specify the destination directory for ddev ssh and the +# directory in which commands passed into ddev exec are run. + +# omit_containers: [db, ddev-ssh-agent] +# Currently only these containers are supported. Some containers can also be +# omitted globally in the ~/.ddev/global_config.yaml. Note that if you omit +# the "db" container, several standard features of DDEV that access the +# database container will be unusable. In the global configuration it is also +# possible to omit ddev-router, but not here. + +# performance_mode: "global" +# DDEV offers performance optimization strategies to improve the filesystem +# performance depending on your host system. Should be configured globally. +# +# If set, will override the global config. Possible values are: +# - "global": uses the value from the global config. +# - "none": disables performance optimization for this project. +# - "mutagen": enables Mutagen for this project. +# - "nfs": enables NFS for this project. +# +# See https://ddev.readthedocs.io/en/stable/users/install/performance/#nfs +# See https://ddev.readthedocs.io/en/stable/users/install/performance/#mutagen + +# fail_on_hook_fail: False +# Decide whether 'ddev start' should be interrupted by a failing hook + +# host_https_port: "59002" +# The host port binding for https can be explicitly specified. It is +# dynamic unless otherwise specified. +# This is not used by most people, most people use the *router* instead +# of the localhost port. + +# host_webserver_port: "59001" +# The host port binding for the ddev-webserver can be explicitly specified. It is +# dynamic unless otherwise specified. +# This is not used by most people, most people use the *router* instead +# of the localhost port. + +# host_db_port: "59002" +# The host port binding for the ddev-dbserver can be explicitly specified. It is dynamic +# unless explicitly specified. + +# mailpit_http_port: "8025" +# mailpit_https_port: "8026" +# The Mailpit ports can be changed from the default 8025 and 8026 + +# host_mailpit_port: "8025" +# The mailpit port is not normally bound on the host at all, instead being routed +# through ddev-router, but it can be bound directly to localhost if specified here. + +# webimage_extra_packages: [php7.4-tidy, php-bcmath] +# Extra Debian packages that are needed in the webimage can be added here + +# dbimage_extra_packages: [telnet,netcat] +# Extra Debian packages that are needed in the dbimage can be added here + +# use_dns_when_possible: true +# If the host has internet access and the domain configured can +# successfully be looked up, DNS will be used for hostname resolution +# instead of editing /etc/hosts +# Defaults to true + +# project_tld: ddev.site +# The top-level domain used for project URLs +# The default "ddev.site" allows DNS lookup via a wildcard +# If you prefer you can change this to "ddev.local" to preserve +# pre-v1.9 behavior. + +# ngrok_args: --basic-auth username:pass1234 +# Provide extra flags to the "ngrok http" command, see +# https://ngrok.com/docs/ngrok-agent/config or run "ngrok http -h" + +# disable_settings_management: false +# If true, DDEV will not create CMS-specific settings files like +# Drupal's settings.php/settings.ddev.php or TYPO3's additional.php +# In this case the user must provide all such settings. + +# You can inject environment variables into the web container with: +# web_environment: +# - SOMEENV=somevalue +# - SOMEOTHERENV=someothervalue + +# no_project_mount: false +# (Experimental) If true, DDEV will not mount the project into the web container; +# the user is responsible for mounting it manually or via a script. +# This is to enable experimentation with alternate file mounting strategies. +# For advanced users only! + +# bind_all_interfaces: false +# If true, host ports will be bound on all network interfaces, +# not the localhost interface only. This means that ports +# will be available on the local network if the host firewall +# allows it. + +# default_container_timeout: 120 +# The default time that DDEV waits for all containers to become ready can be increased from +# the default 120. This helps in importing huge databases, for example. + +#web_extra_exposed_ports: +#- name: nodejs +# container_port: 3000 +# http_port: 2999 +# https_port: 3000 +#- name: something +# container_port: 4000 +# https_port: 4000 +# http_port: 3999 +# Allows a set of extra ports to be exposed via ddev-router +# Fill in all three fields even if you don’t intend to use the https_port! +# If you don’t add https_port, then it defaults to 0 and ddev-router will fail to start. +# +# The port behavior on the ddev-webserver must be arranged separately, for example +# using web_extra_daemons. +# For example, with a web app on port 3000 inside the container, this config would +# expose that web app on https://.ddev.site:9999 and http://.ddev.site:9998 +# web_extra_exposed_ports: +# - name: myapp +# container_port: 3000 +# http_port: 9998 +# https_port: 9999 + +#web_extra_daemons: +#- name: "http-1" +# command: "/var/www/html/node_modules/.bin/http-server -p 3000" +# directory: /var/www/html +#- name: "http-2" +# command: "/var/www/html/node_modules/.bin/http-server /var/www/html/sub -p 3000" +# directory: /var/www/html + +# override_config: false +# By default, config.*.yaml files are *merged* into the configuration +# But this means that some things can't be overridden +# For example, if you have 'use_dns_when_possible: true'' you can't override it with a merge +# and you can't erase existing hooks or all environment variables. +# However, with "override_config: true" in a particular config.*.yaml file, +# 'use_dns_when_possible: false' can override the existing values, and +# hooks: +# post-start: [] +# or +# web_environment: [] +# or +# additional_hostnames: [] +# can have their intended affect. 'override_config' affects only behavior of the +# config.*.yaml file it exists in. + +# Many DDEV commands can be extended to run tasks before or after the +# DDEV command is executed, for example "post-start", "post-import-db", +# "pre-composer", "post-composer" +# See https://ddev.readthedocs.io/en/stable/users/extend/custom-commands/ for more +# information on the commands that can be extended and the tasks you can define +# for them. Example: +#hooks: +# post-import-db: +# - exec: drush sql:sanitize +# - exec: drush updatedb +# - exec: drush cache:rebuild diff --git a/.ddev/docker-compose.clamav.yaml b/.ddev/docker-compose.clamav.yaml new file mode 100644 index 000000000..5ce3d90d8 --- /dev/null +++ b/.ddev/docker-compose.clamav.yaml @@ -0,0 +1,21 @@ +# ClamAV daemon for the drupal/clamav module (hard dependency of tide_core). +# Replaces the `clamav` service from the legacy Ahoy docker-compose stack. +# The hostname override to this service is applied by `ddev install-site`. +services: + clamav: + container_name: ddev-${DDEV_SITENAME}-clamav + hostname: clamav + image: clamav/clamav-debian:stable + expose: + - "3310" + environment: + - CLAMAV_NO_MILTERD=true + labels: + com.ddev.site-name: ${DDEV_SITENAME} + com.ddev.approot: ${DDEV_APPROOT} + volumes: + - clamav:/var/lib/clamav + - ".:/mnt/ddev_config" + +volumes: + clamav: diff --git a/.ddev/docker-compose.elasticsearch.yaml b/.ddev/docker-compose.elasticsearch.yaml new file mode 100644 index 000000000..3625600eb --- /dev/null +++ b/.ddev/docker-compose.elasticsearch.yaml @@ -0,0 +1,28 @@ +#ddev-generated +services: + elasticsearch: + container_name: ddev-${DDEV_SITENAME}-elasticsearch + hostname: ${DDEV_SITENAME}-elasticsearch + image: ${ELASTICSEARCH_DOCKER_IMAGE:-elasticsearch:7.17.14} + expose: + - "9200" + - "9300" + environment: + - cluster.name=docker-cluster + - discovery.type=single-node + - bootstrap.memory_lock=true + - "ES_JAVA_OPTS=-Xms512m -Xmx512m" + - VIRTUAL_HOST=${DDEV_HOSTNAME} + - HTTP_EXPOSE=9200:9200 + - HTTPS_EXPOSE=9201:9200 + labels: + com.ddev.site-name: ${DDEV_SITENAME} + com.ddev.approot: ${DDEV_APPROOT} + volumes: + - elasticsearch:/usr/share/elasticsearch/data + - ".:/mnt/ddev_config" + healthcheck: + test: ["CMD-SHELL", "curl --fail -s elasticsearch:9200"] + +volumes: + elasticsearch: diff --git a/.ddev/docker-compose.selenium-chrome.yaml b/.ddev/docker-compose.selenium-chrome.yaml new file mode 100644 index 000000000..9da12f422 --- /dev/null +++ b/.ddev/docker-compose.selenium-chrome.yaml @@ -0,0 +1,37 @@ +#ddev-generated +# Remove the line above if you don't want this file to be overwritten when you run +# ddev get ddev/ddev-selenium-standalone-chrome +# +# This file comes from https://github.com/ddev/ddev-selenium-standalone-chrome +# +services: + selenium-chrome: + image: selenium/standalone-chromium:138.0 + container_name: ddev-${DDEV_SITENAME}-selenium-chrome + expose: + # The internal noVNC port, which operates over HTTP so it can be exposed + # through the router. + - 7900 + environment: + - VIRTUAL_HOST=$DDEV_HOSTNAME + - HTTPS_EXPOSE=7900:7900 + - HTTP_EXPOSE=7910:7900 + - VNC_NO_PASSWORD=1 + # Enables multiple parallel connections to the Selenium. + - SE_NODE_MAX_SESSIONS=12 + - SE_NODE_OVERRIDE_MAX_SESSIONS=true + # To enable VNC access for traditional VNC clients like macOS "Screen Sharing", + # uncomment the following two lines. + #ports: + # - "5900:5900" + labels: + com.ddev.site-name: ${DDEV_SITENAME} + com.ddev.approot: $DDEV_APPROOT + volumes: + # To enable file uploads in E2E tests. + - ${DDEV_APPROOT}:/var/www/html:r + - ".:/mnt/ddev_config" + + web: + links: + - selenium-chrome diff --git a/.ddev/docker-compose.selenium-chrome_extras.yaml b/.ddev/docker-compose.selenium-chrome_extras.yaml new file mode 100644 index 000000000..4b8e2336f --- /dev/null +++ b/.ddev/docker-compose.selenium-chrome_extras.yaml @@ -0,0 +1,5 @@ +#ddev-generated +services: + selenium-chrome: + external_links: + - "ddev-router:${DDEV_PROJECT}.${DDEV_TLD}" diff --git a/.ddev/elasticsearch/config/elasticsearch8.yml b/.ddev/elasticsearch/config/elasticsearch8.yml new file mode 100644 index 000000000..3a612d3db --- /dev/null +++ b/.ddev/elasticsearch/config/elasticsearch8.yml @@ -0,0 +1,15 @@ +#ddev-generated +# This file contains the configuration settings for Elasticsearch 8. +# For more information, see https://www.elastic.co/guide/en/elasticsearch/reference/current/settings.html + +# https://www.elastic.co/guide/en/elasticsearch/reference/current/important-settings.html#cluster-name +cluster.name: "docker-cluster" + +# https://www.elastic.co/guide/en/elasticsearch/reference/current/important-settings.html#network.host +network.host: 0.0.0.0 + +# Disable security features +# https://www.elastic.co/guide/en/elasticsearch/reference/current/security-settings.html#general-security-settings +xpack.security.enabled: false +xpack.security.autoconfiguration.enabled: false +xpack.security.enrollment.enabled: false diff --git a/.ddev/elasticsearch/docker-compose.elasticsearch8.yaml b/.ddev/elasticsearch/docker-compose.elasticsearch8.yaml new file mode 100644 index 000000000..9e864c365 --- /dev/null +++ b/.ddev/elasticsearch/docker-compose.elasticsearch8.yaml @@ -0,0 +1,6 @@ +#ddev-generated +services: + elasticsearch: + image: ${ELASTICSEARCH_DOCKER_IMAGE:-elasticsearch:8.10.2} + volumes: + - ./elasticsearch/config/elasticsearch8.yml:/usr/share/elasticsearch/config/elasticsearch.yml diff --git a/.docker/Caddyfile b/.docker/Caddyfile deleted file mode 100644 index 50311132c..000000000 --- a/.docker/Caddyfile +++ /dev/null @@ -1,9 +0,0 @@ -content-sdp.docker.internal:80 { - reverse_proxy nginx:8080 -} -elasticsearch.docker.internal:80 { - reverse_proxy elasticsearch:9200 -} -mailhog.docker.internal:80 { - reverse_proxy mailhog:8025 -} diff --git a/.docker/Dockerfile.cli b/.docker/Dockerfile.cli deleted file mode 100644 index 3a5a424fa..000000000 --- a/.docker/Dockerfile.cli +++ /dev/null @@ -1,27 +0,0 @@ -ARG BAY_IMAGE_VERSION - -# @see https://github.com/dpc-sdp/bay/blob/master/bay/images/Dockerfile.php -FROM ghcr.io/dpc-sdp/bay/php-cli:${BAY_IMAGE_VERSION} -ARG COMPOSER - -ENV MYSQL_HOST=mariadb \ - COMPOSER=${COMPOSER:-composer.json} \ - DRUPAL_MODULE_PREFIX=${DRUPAL_MODULE_PREFIX:-} - - - -# Antivirus update returns non-zero codes. -# @see https://github.com/clamwin/clamav/blob/0.100.1/freshclam/freshclamcodes.h#L23 -RUN freshclam --no-warnings || true - -ADD patches /app/patches -ADD scripts /app/scripts -ADD dpc-sdp /app/dpc-sdp - -COPY composer.json .env composer.* /app/ - -RUN echo "memory_limit=-1" >> /usr/local/etc/php/conf.d/memory.ini \ - && COMPOSER=$COMPOSER composer install -n --no-dev --ansi --prefer-dist --no-suggest --optimize-autoloader \ - && rm -rf /usr/local/etc/php/conf.d/memory.ini - -COPY . /app diff --git a/.docker/Dockerfile.elasticsearch b/.docker/Dockerfile.elasticsearch deleted file mode 100644 index ac10da6ff..000000000 --- a/.docker/Dockerfile.elasticsearch +++ /dev/null @@ -1,13 +0,0 @@ -ARG BAY_IMAGE_VERSION -ARG ES_TPL - -FROM "ghcr.io/dpc-sdp/bay/elasticsearch:${BAY_IMAGE_VERSION}" - -ENV ES_TPL=${ES_TPL:-elasticsearch.yml} - -COPY .docker/elasticsearch.yml .docker/elasticsearch.* /tmp/elasticsearch/ - -RUN mkdir -p config \ - && cp /tmp/elasticsearch/$ES_TPL config/elasticsearch.yml - -ENV ES_JAVA_OPTS "-Xms1000m -Xmx1000m" diff --git a/.docker/Dockerfile.nginx-drupal b/.docker/Dockerfile.nginx-drupal deleted file mode 100644 index 3af9d20af..000000000 --- a/.docker/Dockerfile.nginx-drupal +++ /dev/null @@ -1,10 +0,0 @@ -# @see https://github.com/dpc-sdp/bay/blob/master/bay/images/Dockerfile.nginx -ARG BAY_IMAGE_VERSION -ARG CLI_IMAGE -FROM ${CLI_IMAGE:-cli} as cli - -FROM "ghcr.io/dpc-sdp/bay/nginx:${BAY_IMAGE_VERSION}" - -ENV WEBROOT=docroot - -COPY --from=cli /app /app diff --git a/.docker/Dockerfile.php b/.docker/Dockerfile.php deleted file mode 100644 index d36d5e056..000000000 --- a/.docker/Dockerfile.php +++ /dev/null @@ -1,8 +0,0 @@ -# @see https://github.com/dpc-sdp/bay/blob/master/bay/images/Dockerfile.php -ARG BAY_IMAGE_VERSION -ARG CLI_IMAGE -FROM ${CLI_IMAGE:-cli} as cli - -FROM ghcr.io/dpc-sdp/bay/php-fpm:${BAY_IMAGE_VERSION} - -COPY --from=cli /app /app diff --git a/.docker/elasticsearch.ci.yml b/.docker/elasticsearch.ci.yml deleted file mode 100644 index b5bcfad63..000000000 --- a/.docker/elasticsearch.ci.yml +++ /dev/null @@ -1,14 +0,0 @@ -cluster.name: "docker-cluster" -network.host: 0.0.0.0 -transport.host: localhost - -discovery.type: single-node -xpack.license.self_generated.type: basic -xpack.security.enabled: false - -path.repo: ["/usr/share/elasticsearch/data/snapshots"] - -http.cors.enabled: true -http.cors.allow-origin: "*" -http.cors.allow-methods : OPTIONS, HEAD, GET -http.cors.allow-headers : X-Requested-With, X-Auth-Token, Content-Type, Content-Length diff --git a/.docker/elasticsearch.yml b/.docker/elasticsearch.yml deleted file mode 100644 index bcdc1d195..000000000 --- a/.docker/elasticsearch.yml +++ /dev/null @@ -1,13 +0,0 @@ -cluster.name: "docker-cluster" -network.host: 0.0.0.0 - -discovery.type: single-node -xpack.license.self_generated.type: basic -xpack.security.enabled: false - -path.repo: ["/usr/share/elasticsearch/data/snapshots"] - -http.cors.enabled: true -http.cors.allow-origin: "*" -http.cors.allow-methods : OPTIONS, HEAD, GET -http.cors.allow-headers : X-Requested-With, X-Auth-Token, Content-Type, Content-Length diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 7b0657504..000000000 --- a/.dockerignore +++ /dev/null @@ -1,11 +0,0 @@ -.circleci -.git -.idea -*.md -docker-compose* -screenshots -node_modules -vendor -/composer.build.lock -/composer.lock -docroot diff --git a/.editorconfig b/.editorconfig index d97fcd755..686c443ce 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,2 +1,17 @@ -[composer.{json,lock,dev.json}] +# Drupal editor configuration normalization +# @see http://editorconfig.org/ + +# This is the top-most .editorconfig file; do not search in parent directories. +root = true + +# All files. +[*] +end_of_line = LF +indent_style = space +indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[composer.{json,lock}] indent_size = 4 diff --git a/.env b/.env deleted file mode 100644 index 092eb528e..000000000 --- a/.env +++ /dev/null @@ -1,62 +0,0 @@ -## -# File with per-project environment variables. -# -# It is used by Ahoy and other scripts to read default values. -# -# The values must be scalar (cannot be another variable). This file must have -# at least 1 valid (exportable) assignment of value. -# -# You may create .env.local file and store your local environment variables -# there - it will also be loaded by Ahoy and it is excluded from git. -# -# This file may be left as is to assume default values specified in .ahoy.yml. - -# Project name. -# Defaults to the name of the current directory. -# PROJECT_NAME=mysite - -# Docker Compose project name. -# All containers will have this prefix. -# Defaults to $PROJECT_NAME -# COMPOSE_PROJECT_NAME=mysite - -# Drupal module prefix for site-specific modules. -# DRUPAL_MODULE_PREFIX=mysite - -# Path to the root of the project inside of the container. -# APP=/app - -# Path to the site installation relative to the current directory. -# WEBROOT=docroot - -# Local development URL. -# Make sure that there is no trailing slash in the value. -# Defaults to http://.docker.amazee.io -# LOCALDEV_URL=http://mysite.docker.amazee.io - -# Database connection details. -# MYSQL_HOST=mariadb -# MYSQL_PORT=3306 - -# Change to composer.json for site build. -COMPOSER=composer.build.json - -# Uncomment this for site build and set the profile name. -# DRUPAL_PROFILE= - -# Uncomment to run the build in suggest mode. -# In this mode, modules from "suggested" part of composer.json will be -# installed. -# INSTALL_SUGGEST=1 - -# Uncomment to install brand new site. -INSTALL_NEW_SITE=1 - -# Uncomment to refresh search API on deployment. -# DRUPAL_REFRESH_SEARCHAPI=1 - -# Uncomment and add comma-separated list of targets (no spaces). -# PHPCS_TARGETS="src,tests" - -# Uncomment below if Bay integration is enabled. -# BAY_INTEGRATION_ENABLED=1 diff --git a/.gitattributes b/.gitattributes index fb375e1bd..e7b792f84 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,13 +1,64 @@ -# Ignore files for distribution archives. -.gitattributes export-ignore -.gitignore export-ignore -.github export-ignore -.circleci export-ignore -.circleci/config.yml export-ignore -.env export-ignore -dev-init.sh export-ignore - -# linguist configuration for syntax highlighting in github UI. -*.module linguist-language=PHP -*.install linguist-language=PHP -*.inc linguist-language=PHP +# Drupal git normalization +# @see https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html +# @see https://www.drupal.org/node/1542048 + +# Normally these settings would be done with macro attributes for improved +# readability and easier maintenance. However macros can only be defined at the +# repository root directory. Drupal avoids making any assumptions about where it +# is installed. + +# Define text file attributes. +# - Treat them as text. +# - Ensure no CRLF line-endings, neither on checkout nor on checkin. +# - Detect whitespace errors. +# - Exposed by default in `git diff --color` on the CLI. +# - Validate with `git diff --check`. +# - Deny applying with `git apply --whitespace=error-all`. +# - Fix automatically with `git apply --whitespace=fix`. + +*.config text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.css text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.dist text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.engine text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.html text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=html +*.inc text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.install text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.js text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.json text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.lock text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.map text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.md text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.module text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.php text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.po text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.profile text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.script text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.sh text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.sql text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.svg text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.theme text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 diff=php linguist-language=php +*.twig text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.txt text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.xml text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 +*.yml text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tab-in-indent,tabwidth=2 + +# PHPStan's baseline uses tabs instead of spaces. +core/.phpstan-baseline.php text eol=lf whitespace=blank-at-eol,-blank-at-eof,-space-before-tab,tabwidth=2 diff=php linguist-language=php + +# Define binary file attributes. +# - Do not treat them as text. +# - Include binary diff in patches instead of "binary files differ." +*.eot -text diff +*.exe -text diff +*.gif -text diff +*.gz -text diff +*.ico -text diff +*.jpeg -text diff +*.jpg -text diff +*.otf -text diff +*.phar -text diff +*.png -text diff +*.svgz -text diff +*.ttf -text diff +*.woff -text diff +*.woff2 -text diff diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b1ba3e166..6d1285c62 100755 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -4,15 +4,104 @@ on: push: workflow_dispatch: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: tide_build: name: tide_build - secrets: inherit - uses: dpc-sdp/github-actions/.github/workflows/tide_build.yml@v1.6.5 - with: - module_build: true - runner: biggy-tide - export_config: - name: export_config - secrets: inherit - uses: dpc-sdp/github-actions/.github/workflows/export_config.yml@v1.6.5 + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Validate composer configuration + run: composer validate --ansi --strict --no-check-all --no-check-lock + + - name: Install DDEV + uses: ddev/github-action-setup-ddev@v1 + with: + autostart: false + + - name: Start DDEV + run: ddev start -y + + - name: Build Drupal site (poser) + run: ddev poser + + - name: Symlink module into the site + run: ddev symlink-project + + - name: Install site + run: ddev install-site + + - name: Lint code + run: ddev exec vendor/bin/phpcs . + + - name: Run PHPUnit unit tests + run: ddev exec vendor/bin/phpunit --testsuite unit + + - name: Run Behat tests (tide_core) + run: ddev exec vendor/bin/behat --strict --colors --tags='~@skipped' tests + + - name: Run Behat tests (tide_webform) + run: | + ddev drush en -y tide_webform + ddev exec vendor/bin/behat --strict --colors --tags='~@skipped' modules/tide_webform + + - name: Run Behat tests (tide_media) + run: | + ddev drush en -y tide_media + ddev exec vendor/bin/behat --strict --colors --tags='~@skipped' modules/tide_media + + - name: Run Behat tests (tide_event) + run: | + ddev drush en -y tide_event + ddev exec vendor/bin/behat --strict --colors --tags='~@skipped' modules/tide_event + + - name: Run Behat tests (tide_grant) + run: | + ddev drush en -y tide_grant + ddev exec vendor/bin/behat --strict --colors --tags='~@skipped' modules/tide_grant + + - name: Run Behat tests (tide_news) + run: | + ddev drush en -y tide_news + ddev exec vendor/bin/behat --strict --colors --tags='~@skipped' modules/tide_news + + - name: Run Behat tests (tide_landing_page) + run: | + ddev drush en -y tide_landing_page + ddev exec vendor/bin/behat --strict --colors --tags='~@skipped' modules/tide_landing_page + + - name: Run Behat tests (tide_site) + run: | + ddev drush en -y tide_site + ddev exec vendor/bin/behat --strict --colors modules/tide_site + + - name: Run Behat tests (tide_ui_restriction) + run: | + ddev drush en -y tide_ui_restriction + ddev exec vendor/bin/behat --strict --colors modules/tide_ui_restriction + + - name: Run Behat tests (tide_api) + run: | + ddev drush en -y tide_api tide_content_collection_ui tide_share_link + ddev exec vendor/bin/behat --strict --colors --tags='~@skipped' modules/tide_api + + - name: Upload Behat screenshots + if: failure() + uses: actions/upload-artifact@v4 + with: + name: behat-screenshots + path: screenshots + if-no-files-found: ignore + + # TODO: export_config previously used the ahoy-based reusable workflow + # (dpc-sdp/github-actions/.github/workflows/export_config.yml) and needs a + # DDEV-based rewrite before this branch is merged. + # export_config: + # name: export_config + # secrets: inherit + # uses: dpc-sdp/github-actions/.github/workflows/export_config.yml@v1.6.5 diff --git a/.gitignore b/.gitignore index d176ddf50..332b7338a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,12 +2,16 @@ # https://help.github.com/articles/ignoring-files/#create-a-global-gitignore /composer.lock +/composer.contrib.json +/composer.contrib.lock +/web /composer.build.json /composer.build.lock /docroot /dpc-sdp .idea .env.local +.phpunit.result.cache docker-compose.override.yml screenshots vendor diff --git a/Brewfile b/Brewfile deleted file mode 100644 index 0b8e8ea44..000000000 --- a/Brewfile +++ /dev/null @@ -1,8 +0,0 @@ -tap "caskroom/cask" -tap "homebrew/bundle" -tap "homebrew/core" -tap "homebrew/php" -brew "docker" -brew "gpatch" -brew "nvm" -cask "docker" diff --git a/README.md b/README.md index 55a27bc66..e8ebbffd8 100644 --- a/README.md +++ b/README.md @@ -50,13 +50,25 @@ is a maintainer of this package. [Open an issue](https://github.com/dpc-sdp) on GitHub or submit a pull request with suggested changes. ## Development and maintenance -Development is powered by [Dev-Tools](https://github.com/dpc-sdp/dev-tools). Please refer to Dev-Tools' -page for [system requirements](https://github.com/dpc-sdp/dev-tools/#prerequisites) and other details. +Local development is powered by [DDEV](https://ddev.readthedocs.io/) with the +[ddev-drupal-contrib](https://github.com/ddev/ddev-drupal-contrib) add-on. The module +repository is the project root; a disposable Drupal site is built into `web/` and the +module is made available to it via per-file symlinks — code changes at the repository +root take effect immediately, no sync step required. -To start local development stack: -1. Checkout this project -2. Run `./dev-tools.sh` -3. Run `ahoy build` +To start the local development stack: +1. Checkout this project. +2. Run `ddev start` — starts web, db, elasticsearch, selenium-chrome and clamav services. +3. Run `ddev poser` — installs Drupal core (version pinned by `DRUPAL_CORE` in `.ddev/config.yaml`) plus all module dependencies into `web/` and `vendor/`. +4. Run `ddev symlink-project` — symlinks this module into `web/modules/custom/tide_core` (re-run after adding/removing root-level files; also runs automatically on `ddev start`). +5. Run `ddev install-site` — installs a fresh site (`testing` profile) and enables `tide_core` and `tide_test`. + +Day-to-day commands: +- `ddev drush ` — run Drush. +- `ddev phpunit --testsuite unit` — run PHPUnit unit tests. +- `ddev exec vendor/bin/behat --strict --colors [path/to.feature]` — run Behat tests (add `--profile=suggest` for the suggest profile). +- `ddev phpcs` / `ddev phpcbf` — lint / auto-fix coding standards. +- `ddev ssh` — shell into the web container. ## Related projects - [tide](https://github.com/dpc-sdp/tide) diff --git a/behat.yml b/behat.yml index 523d379ef..8c444a085 100644 --- a/behat.yml +++ b/behat.yml @@ -33,22 +33,22 @@ default: Drupal\MinkExtension: files_path: '%paths.base%/tests/behat/fixtures' selenium2: - wd_host: "http://chrome:4444/wd/hub" + wd_host: "http://selenium-chrome:4444/wd/hub" capabilities: { "browser": "chrome", "version": "*", "marionette": true } browserkit_http: ~ ajax_timeout: 5 browser_name: chrome javascript_session: selenium2 - base_url: http://nginx:8080 + base_url: http://web # Provides integration with Drupal APIs. Drupal\DrupalExtension: blackbox: ~ api_driver: drupal drush_driver: drush drupal: - drupal_root: docroot + drupal_root: web drush: - root: docroot + root: web selectors: message_selector: '.messages' error_message_selector: '.messages.messages--error' @@ -80,7 +80,7 @@ default: purge: false Imbo\BehatApiExtension: apiClient: - base_uri: http://nginx:8080 + base_uri: http://web timeout: 5.0 verify: false # Set to true if using production HTTPS locally diff --git a/composer.dev.json b/composer.dev.json deleted file mode 100644 index 875ffc315..000000000 --- a/composer.dev.json +++ /dev/null @@ -1,482 +0,0 @@ -{ - "name": "myorg/mysite", - "description": "Drupal 8 site for MYSITE", - "license": "proprietary", - "require": { - "composer/installers": "^1.9", - "cweagans/composer-patches": "^1.6.0", - "drupal/core-composer-scaffold": "^11.0", - "drupal/core": "10.6.x", - "drupal/core-recommended": "10.6.x", - "drush/drush": "^13", - "vlucas/phpdotenv": "^4.1", - "webflo/drupal-finder": "^1.0" - }, - "require-dev": { - "behat/behat": "~3.5", - "behat/mink": "^1.11", - "behat/mink-browserkit-driver": "^2.2", - "behat/mink-selenium2-driver": "^1.4", - "chi-teck/drupal-code-generator": "^3.0", - "squizlabs/php_codesniffer": "3.7.1", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7", - "doctrine/persistence": "^3.3", - "drupal/coder": "^8.3.10", - "drupal/drupal-extension": "^5.0.0rc1", - "drevops/behat-screenshot": "^1.0", - "drevops/behat-steps": "^1.2.0", - "phpcompatibility/php-compatibility": "^9.1", - "imbo/behat-api-extension": "^6.0", - "symfony/filesystem": "^6.2", - "php-http/curl-client": "*", - "phpunit/phpunit": "^8.5.14 || ^9", - "drupal/core-dev":"10.6.x", - "phpspec/prophecy-phpunit":"^2", - "weitzman/drupal-test-traits": "^1.5", - "php-http/message": "~1.13", - "php-http/message-factory": "^1.1", - "guzzlehttp/psr7": "~2.8.0", - "dpc-sdp/tide_test": "^4.0.0", - "drupal/test_helpers": "^1.2" - }, - "provide": { - "drupal/ckeditor": "*" - }, - "autoload": { - "psr-4": { - "Tide\\Tests\\Context\\": "tests/behat/bootstrap/" - }, - "classmap": [ - "scripts/composer/" - ] - }, - "prefer-stable": true, - "minimum-stability": "dev", - "config": { - "process-timeout": 0, - "sort-packages": true, - "allow-plugins": { - "composer/installers": true, - "oomphinc/composer-installers-extender": true, - "drupal/*": true, - "cweagans/composer-patches": true, - "zaporylie/composer-drupal-optimizations": false, - "dealerdirect/phpcodesniffer-composer-installer": true, - "php-http/discovery": true, - "phpstan/extension-installer": true - } - }, - "repositories": { - "drupal": { - "type": "composer", - "url": "https://packages.drupal.org/8", - "exclude": ["drupal/token_conditions", "drupal/create_menus_permission"] - }, - "asset-packagist": { - "type": "composer", - "url": "https://asset-packagist.org" - }, - "dpc-sdp/tide": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide.git" - }, - "drupal/create_menus_permission": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/create_menus_permission.git" - }, - "dpc-sdp/tide_alert": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_alert.git" - }, - "dpc-sdp/tide_api": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_api.git" - }, - "dpc-sdp/tide_authenticated_content": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_authenticated_content.git" - }, - "dpc-sdp/tide_core": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_core.git" - }, - "dpc-sdp/tide_data_driven_component": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_data_driven_component.git" - }, - "dpc-sdp/tide_demo_content": { - "url": "https://github.com/dpc-sdp/tide_demo_content.git", - "type": "vcs", - "no-api": true - }, - "dpc-sdp/tide_event": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_event.git" - }, - "dpc-sdp/tide_event_atdw": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_event_atdw.git" - }, - "dpc-sdp/tide_grant": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_grant.git" - }, - "dpc-sdp/tide_landing_page": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_landing_page.git" - }, - "dpc-sdp/tide_media": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_media.git" - }, - "dpc-sdp/tide_monsido": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_monsido.git" - }, - "dpc-sdp/tide_page": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_page.git" - }, - "dpc-sdp/tide_profile": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_profile.git" - }, - "dpc-sdp/tide_publication": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_publication.git" - }, - "dpc-sdp/tide_news": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_news.git" - }, - "dpc-sdp/tide_search": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_search.git" - }, - "dpc-sdp/tide_site": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_site.git" - }, - "dpc-sdp/tide_test": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_test.git" - }, - "dpc-sdp/tide_webform": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/tide_webform.git" - }, - "library/dropzone": { - "type": "package", - "package": { - "name": "library/dropzone", - "version": "4.3.0", - "type": "drupal-library", - "dist": { - "url": "https://github.com/enyo/dropzone/archive/v4.3.0.zip", - "type": "zip" - } - } - }, - "ckeditor.autogrow": { - "_webform": true, - "type": "package", - "package": { - "name": "ckeditor/autogrow", - "version": "4.16.1", - "type": "drupal-library", - "dist": { - "url": "https://download.ckeditor.com/autogrow/releases/autogrow_4.16.1.zip", - "type": "zip" - }, - "require": { - "composer/installers": "~1.0" - } - } - }, - "ckeditor.codemirror": { - "_webform": true, - "type": "package", - "package": { - "name": "ckeditor/codemirror", - "version": "v1.17.12", - "type": "drupal-library", - "dist": { - "url": "https://github.com/w8tcha/CKEditor-CodeMirror-Plugin/releases/download/v1.17.12/CKEditor-CodeMirror-Plugin.zip", - "type": "zip" - }, - "require": { - "composer/installers": "~1.0" - } - } - }, - "ckeditor.image": { - "_webform": true, - "type": "package", - "package": { - "name": "ckeditor/image", - "version": "4.16.1", - "type": "drupal-library", - "dist": { - "url": "https://download.ckeditor.com/image/releases/image_4.16.1.zip", - "type": "zip" - }, - "require": { - "composer/installers": "~1.0" - } - } - }, - "ckeditor.link": { - "_webform": true, - "type": "package", - "package": { - "name": "ckeditor/link", - "version": "4.16.1", - "type": "drupal-library", - "dist": { - "url": "https://download.ckeditor.com/link/releases/link_4.16.1.zip", - "type": "zip" - }, - "require": { - "composer/installers": "~1.0" - } - } - }, - "ckeditor/templates": { - "type": "package", - "package": { - "name": "ckeditor/templates", - "type": "drupal-library", - "version": "4.11.1", - "dist": { - "type": "zip", - "url": "https://download.ckeditor.com/templates/releases/templates_4.11.1.zip" - } - } - }, - "jquery.timepicker": { - "_webform": true, - "type": "package", - "package": { - "name": "jquery/timepicker", - "version": "1.14.0", - "type": "drupal-library", - "extra": { - "installer-name": "jquery.timepicker" - }, - "dist": { - "url": "https://github.com/jonthornton/jquery-timepicker/archive/refs/tags/1.14.0.zip", - "type": "zip" - }, - "require": { - "composer/installers": "~1.0" - } - } - }, - "jquery.textcounter": { - "_webform": true, - "type": "package", - "package": { - "name": "jquery/textcounter", - "version": "0.9.1", - "type": "drupal-library", - "extra": { - "installer-name": "jquery.textcounter" - }, - "dist": { - "url": "https://github.com/ractoon/jQuery-Text-Counter/archive/0.9.1.zip", - "type": "zip" - }, - "require": { - "composer/installers": "~1.0" - } - } - }, - "choices": { - "_webform": true, - "type": "package", - "package": { - "name": "choices/choices", - "version": "9.0.1", - "type": "drupal-library", - "extra": { - "installer-name": "choices" - }, - "dist": { - "url": "https://github.com/jshjohnson/Choices/archive/v9.0.1.zip", - "type": "zip" - }, - "require": { - "composer/installers": "~1.0" - } - } - }, - "codemirror": { - "_webform": true, - "type": "package", - "package": { - "name": "codemirror/codemirror", - "version": "5.65.12", - "type": "drupal-library", - "extra": { - "installer-name": "codemirror" - }, - "dist": { - "url": "https://github.com/components/codemirror/archive/5.65.12.zip", - "type": "zip" - }, - "require": { - "composer/installers": "~1.0" - } - } - }, - "jquery.inputmask": { - "_webform": true, - "type": "package", - "package": { - "name": "jquery/inputmask", - "version": "5.0.8", - "type": "drupal-library", - "extra": { - "installer-name": "jquery.inputmask" - }, - "dist": { - "url": "https://github.com/RobinHerbots/jquery.inputmask/archive/5.0.8.zip", - "type": "zip" - }, - "require": { - "composer/installers": "~1.0" - } - } - }, - "jquery.intl-tel-input": { - "_webform": true, - "type": "package", - "package": { - "name": "jquery/intl-tel-input", - "version": "17.0.19", - "type": "drupal-library", - "extra": { - "installer-name": "jquery.intl-tel-input" - }, - "dist": { - "url": "https://github.com/jackocnr/intl-tel-input/archive/v17.0.19.zip", - "type": "zip" - }, - "require": { - "composer/installers": "~1.0" - } - } - }, - "progress-tracker": { - "_webform": true, - "type": "package", - "package": { - "name": "progress-tracker/progress-tracker", - "version": "2.0.7", - "type": "drupal-library", - "extra": { - "installer-name": "progress-tracker" - }, - "dist": { - "url": "https://github.com/NigelOToole/progress-tracker/archive/refs/tags/2.0.7.zip", - "type": "zip" - }, - "require": { - "composer/installers": "~1.0" - } - } - }, - "drupal/token_conditions": { - "type": "vcs", - "no-api": true, - "url": "https://github.com/dpc-sdp/token_conditions.git" - } - }, - "scripts": { - "pre-install-cmd": [ - "DrupalProject\\composer\\ScriptHandler::checkComposerVersion" - ], - "pre-update-cmd": [ - "DrupalProject\\composer\\ScriptHandler::checkComposerVersion" - ], - "post-install-cmd": [ - "composer drupal:scaffold", - "DrupalProject\\composer\\ScriptHandler::createRequiredFiles", - "Utilities\\composer\\DrupalSettings::create" - ], - "post-update-cmd": [ - "composer drupal:scaffold", - "DrupalProject\\composer\\ScriptHandler::createRequiredFiles", - "Utilities\\composer\\DrupalSettings::create" - ] - }, - "extra": { - "drupal-scaffold": { - "allowed-packages": [ - "drupal/core" - ], - "file-mapping": { - "[web-root]/robots.txt": false - }, - "locations": { - "web-root": "./docroot" - } - }, - "enable-patching": true, - "composer-exit-on-patch-failure": true, - "installer-paths": { - "docroot/core": [ - "type:drupal-core" - ], - "docroot/modules/contrib/{$name}": [ - "type:drupal-module" - ], - "docroot/themes/contrib/{$name}": [ - "type:drupal-theme" - ], - "docroot/profiles/contrib/{$name}": [ - "type:drupal-profile" - ], - "docroot/libraries/{$name}": [ - "type:drupal-library" - ], - "drush/contrib/{$name}": [ - "type:drupal-drush" - ] - }, - "preserve-paths": [ - "docroot/modules/custom", - "docroot/themes/custom", - "docroot/drush", - "docroot/sites/default/settings.php", - "docroot/sites/default/default.settings.local.php", - "docroot/sites/default/default.services.local.yml", - "docroot/sites/default/settings.local.php", - "docroot/sites/default/services.local.yml", - "docroot/sites/default/files", - "docroot/robots.txt", - "docroot/.htaccess" - ] - } -} diff --git a/composer.json b/composer.json index 9105c7d10..9146b2db2 100644 --- a/composer.json +++ b/composer.json @@ -469,10 +469,25 @@ } }, "installer-paths": { - "docroot/libraries/{$name}": [ + "web/core": [ + "type:drupal-core" + ], + "web/libraries/{$name}": [ "type:drupal-library", "type:bower-asset", "type:npm-asset" + ], + "web/modules/contrib/{$name}": [ + "type:drupal-module" + ], + "web/profiles/contrib/{$name}": [ + "type:drupal-profile" + ], + "web/themes/contrib/{$name}": [ + "type:drupal-theme" + ], + "drush/{$name}": [ + "type:drupal-drush" ] }, "installer-types": ["bower-asset", "npm-asset"] @@ -480,10 +495,49 @@ "config": { "allow-plugins": { "composer/installers": true, - "oomphinc/composer-installers-extender": true + "oomphinc/composer-installers-extender": true, + "php-http/discovery": true, + "cweagans/composer-patches": true, + "drupal/core-composer-scaffold": true, + "dealerdirect/phpcodesniffer-composer-installer": true, + "phpstan/extension-installer": true, + "tbachert/spi": true }, "audit": { "block-insecure": false } + }, + "require-dev": { + "behat/behat": "~3.5", + "behat/mink": "^1.11", + "behat/mink-browserkit-driver": "^2.2", + "behat/mink-selenium2-driver": "^1.4", + "squizlabs/php_codesniffer": "3.7.1", + "dealerdirect/phpcodesniffer-composer-installer": "^0.7", + "doctrine/persistence": "^3.3", + "drupal/coder": "^8.3.10", + "drupal/drupal-extension": "^5.0.0rc1", + "drevops/behat-screenshot": "^1.0", + "drevops/behat-steps": "^1.2.0", + "drush/drush": "^13", + "phpcompatibility/php-compatibility": "^9.1", + "imbo/behat-api-extension": "^6.0", + "symfony/filesystem": "^6.2", + "php-http/curl-client": "*", + "phpunit/phpunit": "^8.5.14 || ^9", + "phpspec/prophecy-phpunit": "^2", + "weitzman/drupal-test-traits": "^1.5", + "php-http/message": "~1.13", + "php-http/message-factory": "^1.1", + "guzzlehttp/psr7": "~2.8.0", + "drupal/test_helpers": "^1.2" + }, + "provide": { + "drupal/ckeditor": "*" + }, + "autoload-dev": { + "psr-4": { + "Tide\\Tests\\Context\\": "tests/behat/bootstrap/" + } } } diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 0770e3d1a..000000000 --- a/docker-compose.yml +++ /dev/null @@ -1,170 +0,0 @@ -# Docker Compose configuration file. -# -# - Using a single file to work in local, CI and production environments. -# - Local overrides are possible using docker-composer.override.yml file. -# - Use inline comments starting with ### to have the line removed in CI. -x-bay-image-version: - &bay-image-version ${BAY_IMAGE_VERSION:-6.x} - -x-project: - &project ${PROJECT_NAME:-mysite} - -x-volumes: - &default-volumes - # Understanding host mounting in Docker-based projects. - # - # To share application code between services (containers), Docker uses volumes. - # When used in non-development environments, containers have access to - # the same shared files using volumes and these volumes do not need to be - # mounted from the host. But for development environment, when the code - # constantly changes on the host, we need to have these changes synchronized - # into all containers. Since we are using single `docker-compose.yml` file for - # all environments, we have to accommodate both cases, so we are specifying an - # override for the same directory as a mounted volume as a commented-out lines, - # which will be automatically uncommented in CI. - # - # See Docker Compose reference about volumes https://docs.docker.com/compose/compose-file/compose-file-v2/#volume-configuration-reference - volumes: - - .:/app:${VOLUME_FLAGS:-delegated} ### Local overrides to mount host filesystem. Automatically removed in CI and PROD. - - ./docroot/sites/default/files:/app/docroot/sites/default/files:${VOLUME_FLAGS:-delegated} ### Local overrides to mount host filesystem. Automatically removed in CI and PROD. - ##- app:/app - ##- files:/app/docroot/sites/default/files - -x-environment: - &default-environment - LAGOON_PROJECT: *project - # Local dev URL populated from the environment. Do not override here. Instead, - # specify the value in .env file. - LAGOON_LOCALDEV_URL: ${LOCALDEV_URL:-http://mysite.docker.amazee.io} - LAGOON_ROUTE: ${LOCALDEV_URL:-http://mysite.docker.amazee.io} - GITHUB_TOKEN: ${GITHUB_TOKEN:-} - BAY_KEY: ${BAY_KEY:-} - BAY_IMAGE_VERSION: ${BAY_IMAGE_VERSION:-6.x} - LAGOON_ENVIRONMENT_TYPE: ${LAGOON_ENVIRONMENT_TYPE:-local} - DRUPAL_REFRESH_SEARCHAPI: ${DRUPAL_REFRESH_SEARCHAPI:-} - # Uncomment to enable Xdebug and then restart via `ahoy up`. - #XDEBUG_ENABLE: "true" - DOCKERHOST: host.docker.internal - # Comment out the above and uncomment the below if you are using Colima rather than Docker Desktop - #DOCKERHOST: 192.168.5.2 - -services: - - cli: - build: - context: . - dockerfile: .docker/Dockerfile.cli - args: - COMPOSER: ${COMPOSER:-composer.json} - BAY_IMAGE_VERSION: *bay-image-version - image: *project - environment: - << : *default-environment - COMPOSER_MEMORY_LIMIT: -1 - << : *default-volumes - labels: - lagoon.type: cli-persistent - lagoon.persistent: /app/docroot/sites/default/files/ - lagoon.persistent.name: nginx-php - - nginx: - build: - context: . - dockerfile: .docker/Dockerfile.nginx-drupal - args: - CLI_IMAGE: *project - BAY_IMAGE_VERSION: *bay-image-version - << : *default-volumes - environment: - << : *default-environment - depends_on: - - cli - labels: - lagoon.type: nginx-php-persistent - lagoon.persistent: /app/docroot/sites/default/files/ - lagoon.persistent.class: slow - lagoon.name: nginx-php - expose: - - "8080" - ports: - - "8888:8080" - - php: - build: - context: . - dockerfile: .docker/Dockerfile.php - args: - CLI_IMAGE: *project - BAY_IMAGE_VERSION: *bay-image-version - environment: - << : *default-environment - << : *default-volumes - depends_on: - - cli - labels: - lagoon.type: nginx-php-persistent - lagoon.persistent: /app/docroot/sites/default/files/ - lagoon.persistent.class: slow - lagoon.name: nginx-php - - mariadb: - image: "ghcr.io/dpc-sdp/bay/mariadb:${BAY_IMAGE_VERSION:-6.x}" - environment: - << : *default-environment - ports: - - "3306" # Find port on host with `ahoy info` or `docker-compose port mariadb 3306` - labels: - lagoon.type: mariadb-shared - - redis: - image: amazeeio/redis - labels: - lagoon.type: redis - - elasticsearch: - build: - context: . - dockerfile: .docker/Dockerfile.elasticsearch - args: - ES_TPL: ${ES_TPL:-elasticsearch.yml} - BAY_IMAGE_VERSION: *bay-image-version - ports: - - "9200:9200" - labels: - lagoon.type: none - - chrome: - image: seleniarm/standalone-chromium:101.0 - shm_size: '1gb' - environment: - <<: *default-environment - <<: *default-volumes - depends_on: - - cli - labels: - lagoon.type: none - - clamav: - image: "clamav/clamav-debian:stable" - environment: - <<: *default-environment - ports: - - "3310" - labels: - lagoon.type: none - - proxy: - image: caddy:latest - ports: - - "${LOCAL_PORT:-80}:80" - volumes: - - $PWD/.docker:/etc/caddy - - $PWD/.docker/Caddyfile:/etc/caddy/Caddyfile - -networks: - amazeeio-network: - external: true - -volumes: - app: {} - files: {} diff --git a/modules/tide_site/config/install/views.view.media_report.yml b/modules/tide_site/config/optional/views.view.media_report.yml similarity index 100% rename from modules/tide_site/config/install/views.view.media_report.yml rename to modules/tide_site/config/optional/views.view.media_report.yml diff --git a/modules/tide_site/config/install/views.view.tide_content_report.yml b/modules/tide_site/config/optional/views.view.tide_content_report.yml similarity index 100% rename from modules/tide_site/config/install/views.view.tide_content_report.yml rename to modules/tide_site/config/optional/views.view.tide_content_report.yml diff --git a/modules/tide_site/tests/src/Unit/TideSiteMenuAutocreateTest.php b/modules/tide_site/tests/src/Unit/TideSiteMenuAutocreateTest.php index 7e789293a..621d3cd49 100644 --- a/modules/tide_site/tests/src/Unit/TideSiteMenuAutocreateTest.php +++ b/modules/tide_site/tests/src/Unit/TideSiteMenuAutocreateTest.php @@ -78,14 +78,17 @@ public function testMakeMenuName($menu_title, $parents, $expected) { /** * Data provider to test makeMenuName() method. + * + * Menu names are built from term ids (mocked sequentially from 1, ordered + * from the term to the "oldest" parent) and truncated to 32 characters. */ public function providerMakeMenuName() { return [ ['abc', [], 'site-abc'], - ['abc', ['t1'], 'site-abc-t1'], - ['abc', ['t1', 'p1'], 'site-abc-p1-t1'], - ['abc', ['t1', 'p1', 'p2'], 'site-abc-p2-p1-t1'], - ['abc', ['t1', 'p1 p11', 'p2 p21'], 'site-abc-p2-p21-p1-p11-t1'], + ['abc', ['t1'], 'site-abc-1'], + ['abc', ['t1', 'p1'], 'site-abc-2-1'], + ['abc', ['t1', 'p1', 'p2'], 'site-abc-3-2-1'], + ['a very long menu title indeed', ['t1', 'p1'], 'site-a-very-long-menu-title-inde'], ]; } diff --git a/modules/tide_site/tests/src/Unit/TideSiteTest.php b/modules/tide_site/tests/src/Unit/TideSiteTest.php index 581acc06a..994f5e792 100644 --- a/modules/tide_site/tests/src/Unit/TideSiteTest.php +++ b/modules/tide_site/tests/src/Unit/TideSiteTest.php @@ -14,7 +14,7 @@ abstract class TideSiteTest extends UnitTestCase { * * @param array $parents * Array of term and it's parents names, ordered from term to the "oldest" - * parent. + * parent. Mocked term ids are assigned sequentially starting from 1. * * @return array * Array of mocked term objects. @@ -22,9 +22,10 @@ abstract class TideSiteTest extends UnitTestCase { protected function prepareMockTermParents(array $parents) { $list = []; - foreach ($parents as $name) { + foreach (array_values($parents) as $index => $name) { $mock = self::createMock('\Drupal\taxonomy\Entity\Term'); $mock->method('getName')->willReturn($name); + $mock->method('id')->willReturn((string) ($index + 1)); $list[] = $mock; } diff --git a/modules/tide_site/tide_site.install b/modules/tide_site/tide_site.install index 85b837451..754cddc0c 100644 --- a/modules/tide_site/tide_site.install +++ b/modules/tide_site/tide_site.install @@ -185,7 +185,7 @@ function tide_site_update_10005() { $view = View::load('tide_content_report'); if (!$view) { \Drupal::moduleHandler()->loadInclude('tide_core', 'inc', 'includes/helpers'); - $config_location = [\Drupal::service('extension.list.module')->getPath('tide_site') . '/config/install']; + $config_location = [\Drupal::service('extension.list.module')->getPath('tide_site') . '/config/optional']; $config_read = _tide_read_config('views.view.tide_content_report', $config_location, TRUE); $storage = \Drupal::entityTypeManager()->getStorage('view'); $id = $storage->getIDFromConfigName('views.view.tide_content_report', $storage->getEntityType()->getConfigPrefix()); @@ -228,7 +228,7 @@ function tide_site_update_10006() { } \Drupal::moduleHandler()->loadInclude('tide_core', 'inc', 'includes/helpers'); - $config_location = [\Drupal::service('extension.list.module')->getPath('tide_site') . '/config/install']; + $config_location = [\Drupal::service('extension.list.module')->getPath('tide_site') . '/config/optional']; $view_config = _tide_read_config('views.view.tide_content_report', $config_location, FALSE); if (empty($view_config['display']['default']['display_options']['fields']['view_node'])) { return; @@ -243,7 +243,7 @@ function tide_site_update_10006() { */ function tide_site_update_10007() { \Drupal::moduleHandler()->loadInclude('tide_core', 'inc', 'includes/helpers'); - $config_location = [\Drupal::service('extension.list.module')->getPath('tide_site') . '/config/install']; + $config_location = [\Drupal::service('extension.list.module')->getPath('tide_site') . '/config/optional']; $config_read = _tide_read_config('views.view.media_report', $config_location, TRUE); $has_secure_file = (bool) \Drupal::entityTypeManager() diff --git a/phpcs.xml b/phpcs.xml index 14f338705..a217b226e 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -14,7 +14,8 @@ key "config.platform.php".--> - */docroot/* + */web/* + */\.ddev/* */console/* settings.generated.php */vendor/* diff --git a/phpunit.xml.dist b/phpunit.xml.dist index a7bf38ba5..bcd7a0c97 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -4,18 +4,17 @@ or your current system user. See core/tests/README.md and https://www.drupal.org/node/2116263 for details. --> - + - ./dpc-sdp + ./src + ./modules/*/src ./modules/*/src/Tests ./modules/*/tests - ../modules/*/src/Tests - ../modules/*/tests - ../modules/*/*/src/Tests - ../modules/*/*/tests + ./modules/*/*/src/Tests + ./modules/*/*/tests @@ -24,9 +23,9 @@ - + - + diff --git a/scripts/bay-deploy.sh b/scripts/bay-deploy.sh deleted file mode 100755 index f3a256df8..000000000 --- a/scripts/bay-deploy.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash - -BAY_ENDPOINT_DEPLOY=http://rest2tasks-lagoon-master.lagoon.vicsdp.amazee.io/deploy -BAY_BRANCH=${BAY_BRANCH:-$1} - -bay_project=$(awk '$1=="project:"{print $2}' .lagoon.yml) - -if [ -z $BAY_BRANCH ]; then - BAY_BRANCH=$(git symbolic-ref --short -q HEAD) -fi - -branch_sha=$(git ls-remote origin | grep refs/heads/${BAY_BRANCH} | cut -f 1) -[ -z $branch_sha ] && echo "ERROR: Fetching of the remote failed. Please ensure you are connected to the internet and have sufficient permissions to access the repository." && exit 1 - -echo "Project: $bay_project" -echo "Branch: $BAY_BRANCH" -echo "SHA: $branch_sha" - -curl -X POST -d projectName=${bay_project} -d sha=${branch_sha} -d branchName=${BAY_BRANCH} ${BAY_ENDPOINT_DEPLOY} diff --git a/scripts/composer-merge.sh b/scripts/composer-merge.sh deleted file mode 100755 index 4852be303..000000000 --- a/scripts/composer-merge.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash -## -# Build composer config file from current and dev composer files. -# -# @note: Composer merge plugin does not merge all sections, so we are using jq. -# -set -e - -COMPOSER_BUILD=${COMPOSER_BUILD:-composer.build.json} -COMPOSER_DEV=${COMPOSER_DEV:-composer.dev.json} - -echo "==> Create a build composer file" -# @note: gojq needs to exist on host as the merging of Composer config is running -# before containers are started. -cat <<< "$(gojq -s --indent 4 -M 'def merge(a;b): - reduce b[] as $item (a; - reduce ($item | keys[]) as $key (.; - $item[$key] as $val | ($val | type) as $type | .[$key] = if ($type == "object") then - merge({}; [if .[$key] == null then {} else .[$key] end, $val]) - elif ($type == "array") then - (.[$key] + $val | unique) - else - $val - end) - ); - merge({}; .)' composer.json $COMPOSER_DEV )" > ${COMPOSER_BUILD} diff --git a/scripts/composer/DrupalSettings.php b/scripts/composer/DrupalSettings.php deleted file mode 100644 index 433230376..000000000 --- a/scripts/composer/DrupalSettings.php +++ /dev/null @@ -1,216 +0,0 @@ -locateRoot(getcwd()); - $drupalRoot = $drupalFinder->getDrupalRoot(); - - $standard_settings_file = $drupalRoot . '/sites/default/settings.php'; - $generated_settings_file_name = 'settings.generated.php'; - - $defaults = [ - 'mysql_database' => 'drupal', - 'mysql_user' => 'drupal', - 'mysql_password' => 'drupal', - 'mysql_host' => 'localhost', - 'mysql_port' => '', - 'mysql_prefix' => '', - 'settings_path' => $drupalRoot . '/sites/default/' . $generated_settings_file_name, - ]; - - $options = self::extractEnvironmentVariables(array_keys($defaults)) - + self::extractCliOptions($event->getArguments(), array_keys($defaults)) - + $defaults; - - if (!$fs->exists($options['settings_path'])) { - $content = self::getDefaultDrupalSettingsContent($options); - $fs->dumpFile($options['settings_path'], $content); - $fs->chmod($options['settings_path'], 0644); - $event->getIO()->write(sprintf('Created file %s with chmod 0644', $options['settings_path'] . PHP_EOL . $content)); - } - else { - $event->getIO()->write(sprintf('Skipping creation of Drupal settings file "%s" - file already exists', $options['settings_path'])); - } - - // Add inclusion of this file to standard settings file if it exists and - // such inclusion has not been added previously. - if ($fs->exists($options['settings_path']) && $fs->exists($standard_settings_file)) { - if (strpos(file_get_contents($standard_settings_file), $generated_settings_file_name) === FALSE) { - $string = <<getIO()->write(sprintf('Added inclusion of generated settings file %s to %s', $generated_settings_file_name, $standard_settings_file)); - } - else { - $event->getIO()->write(sprintf('Skipped inclusion of generated settings file %s to %s - inclusion already present', $generated_settings_file_name, $standard_settings_file)); - } - } - } - - /** - * Delete Drupal settings file. - */ - public static function delete(Event $event) { - $defaults = [ - 'settings_path' => 'docroot/sites/default/settings.generated.php', - ]; - - $options = self::extractEnvironmentVariables(array_keys($defaults)) - + self::extractCliOptions($event->getArguments(), array_keys($defaults)) - + $defaults; - - $fs = new Filesystem(); - if (!$fs->exists($options['settings_path'])) { - $event->getIO()->write('Skipping deletion of Drupal settings file - file does not exists'); - } - else { - $fs->remove($options['settings_path']); - $event->getIO()->write(sprintf('Deleted file %s', $options['settings_path'])); - } - } - - /** - * Return content for default Drupal settings file. - */ - protected static function getDefaultDrupalSettingsContent($options) { - return << - [ - 'default' => - [ - 'database' => '${options['mysql_database']}', - 'username' => '${options['mysql_user']}', - 'password' => '${options['mysql_password']}', - 'host' => '${options['mysql_host']}', - 'port' => '${options['mysql_port']}', - 'driver' => 'mysql', - 'prefix' => '${options['mysql_prefix']}', - ], - ], -]; - -// Allow installing extensions under tests directory. -\$settings['extension_discovery_scan_tests'] = TRUE; - -FILE; - } - - /** - * Extract options from environment variables. - * - * @param bool|array $allowed - * Array of allowed options. - * - * @return array - * Array of extracted options. - */ - protected static function extractEnvironmentVariables(array $allowed) { - $options = []; - - $dotenv = Dotenv::createImmutable(__DIR__ . '/../..'); - $dotenv->load(); - - foreach ($allowed as $name) { - $value = getenv(strtoupper($name)); - if ($value !== FALSE) { - $options[$name] = $value; - } - } - - return $options; - } - - /** - * Extract options from CLI arguments. - * - * @param array $arguments - * Array of arguments. - * @param bool|array $allowed - * Array of allowed options. - * - * @return array - * Array of extracted options. - */ - protected static function extractCliOptions(array $arguments, array $allowed) { - $options = []; - - foreach ($arguments as $argument) { - if (strpos($argument, '--') === 0) { - [$name, $value] = explode('=', $argument); - $name = substr($name, strlen('--')); - $options[$name] = $value; - if (array_key_exists($name, $allowed) && !is_null($value)) { - $options[$name] = $value; - } - } - } - - return $options; - } - - /** - * Appends content to an existing file. - * - * Polyfill for older versions of Filesystem shipped with Composer phar. - * - * @param string $filename - * The file to which to append content. - * @param string $content - * The content to append. - * - * @throws \Symfony\Component\Filesystem\Exception\IOException - * If the file is not writable. - */ - protected static function appendToFile($filename, $content) { - $fs = new Filesystem(); - - $dir = \dirname($filename); - - if (!is_dir($dir)) { - $fs->mkdir($dir); - } - - if (!is_writable($dir)) { - throw new IOException(sprintf('Unable to write to the "%s" directory.', $dir), 0, NULL, $dir); - } - - if (FALSE === @file_put_contents($filename, $content, FILE_APPEND)) { - throw new IOException(sprintf('Failed to write file "%s".', $filename), 0, NULL, $filename); - } - } - -} diff --git a/scripts/composer/ScriptHandler.php b/scripts/composer/ScriptHandler.php deleted file mode 100644 index ec5d7585d..000000000 --- a/scripts/composer/ScriptHandler.php +++ /dev/null @@ -1,138 +0,0 @@ -locateRoot(getcwd()); - $drupalRoot = $drupalFinder->getDrupalRoot(); - - $dirs = [ - 'modules', - 'profiles', - 'themes', - ]; - - // Required for unit testing. - foreach ($dirs as $dir) { - if (!$fs->exists($drupalRoot . '/' . $dir)) { - $fs->mkdir($drupalRoot . '/' . $dir); - $fs->touch($drupalRoot . '/' . $dir . '/.gitkeep'); - } - } - - // Prepare the settings file for installation. - if ($fs->exists($drupalRoot . '/sites/default/default.settings.php') && !$fs->exists($drupalRoot . '/sites/default/settings.php')) { - $fs->copy($drupalRoot . '/sites/default/default.settings.php', $drupalRoot . '/sites/default/settings.php'); - $event->getIO()->write('Created a sites/default/settings.php file'); - } - - if (!$fs->exists($drupalRoot . '/sites/default/settings.php')) { - $event->getIO()->writeError('Settings file not found'); - exit(1); - } - - $fs->chmod($drupalRoot . '/sites/default', 0777); - $fs->chmod($drupalRoot . '/sites/default/settings.php', 0666); - - $configPath = Path::makeRelative($drupalFinder->getComposerRoot() . '/config/default', $drupalRoot); - $settings_string = <<exists($drupalRoot . '/sites/default/files')) { - $oldmask = umask(0); - $fs->mkdir($drupalRoot . '/sites/default/files', 0777); - umask($oldmask); - $event->getIO()->write('Created a sites/default/files directory with chmod 0777'); - } - } - - /** - * Checks if the installed version of Composer is compatible. - * - * Composer 1.0.0 and higher consider a `composer install` without having a - * lock file present as equal to `composer update`. We do not ship with a lock - * file to avoid merge conflicts downstream, meaning that if a project is - * installed with an older version of Composer the scaffolding of Drupal will - * not be triggered. We check this here instead of in drupal-scaffold to be - * able to give immediate feedback to the end user, rather than failing the - * installation after going through the lengthy process of compiling and - * downloading the Composer dependencies. - * - * @see https://github.com/composer/composer/pull/5035 - */ - public static function checkComposerVersion(Event $event) { - $composer = $event->getComposer(); - $io = $event->getIO(); - - $version = $composer::VERSION; - - // The dev-channel of composer uses the git revision as version number, - // try to the branch alias instead. - if (preg_match('/^[0-9a-f]{40}$/i', $version)) { - $version = $composer::BRANCH_ALIAS_VERSION; - } - - // If Composer is installed through git we have no easy way to determine if - // it is new enough, just display a warning. - if ($version === '@package_version@' || $version === '@package_branch_alias_version@') { - $io->writeError('You are running a development version of Composer. If you experience problems, please update Composer to the latest stable version.'); - } - elseif (Comparator::lessThan($version, '1.0.0')) { - $io->writeError('Drupal-project requires Composer version 1.0.0 or higher. Please update your Composer before continuing.'); - exit(1); - } - } - - /** - * Appends content to an existing file. - * - * Polyfill for older versions of Filesystem shipped with Composer phar. - * - * @param string $filename - * The file to which to append content. - * @param string $content - * The content to append. - * - * @throws \Symfony\Component\Filesystem\Exception\IOException - * If the file is not writable. - */ - protected static function appendToFile($filename, $content) { - $fs = new Filesystem(); - - $dir = \dirname($filename); - - if (!is_dir($dir)) { - $fs->mkdir($dir); - } - - if (!is_writable($dir)) { - throw new \Exception(sprintf('Unable to write to the "%s" directory.', $dir), 0, NULL, $dir); - } - - if (FALSE === @file_put_contents($filename, $content, FILE_APPEND)) { - throw new \Exception(sprintf('Failed to write file "%s".', $filename), 0, NULL, $filename); - } - } - -} diff --git a/scripts/doctor.sh b/scripts/doctor.sh deleted file mode 100755 index 1e0f50abf..000000000 --- a/scripts/doctor.sh +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env bash -# -# Check Dev Tools project requirements. -# - -LOCALDEV_URL=${LOCALDEV_URL:-http://mysite.docker.amazee.io/} - -################################################################################ -#################### DO NOT CHANGE ANYTHING BELOW THIS LINE #################### -################################################################################ - -# -# Main entry point. -# -main() { - # Check project requirements. - status "Checking project requirements" - - [ $(command_exists docker) == "1" ] && error "Please install Docker." && exit 1 - [ $(command_exists docker-compose) == "1" ] && error "Please install docker-compose." && exit 1 - [ $(command_exists composer) == "1" ] && error "Please install composer: visit https://getcomposer.org/" && exit 1 - [ $(command_exists nvm) == "1" ] && error "Please install nvm" && exit 1 - [ $(command_exists pygmy) == "1" ] && error "Please install pygmy" && exit 1 - - [ ! ~/.bash_profile ] && error " ~/.bash_profile does not exist. Run 'touch ~/.bash_profile && source ~/.bashrc'" && exit 1 - - # Check what is listening on port 80. - if ! lsof -i :80 | grep -q LISTEN; then - error "Nothing is listening on port 80. Run 'pygmy up' to start pygmy." && exit 1 - elif ! lsof -i :80 | grep LISTEN | grep -q om.docke; then - error "Port 80 is occupied by other service. Stop this service and run 'pygmy up'" - else - pygmy_status=$(pygmy status) - [ "$?" == "1" ] && error "pygmy is not running. Run 'pygmy up' to start pygmy." && exit 1 - # @todo: Add more checks for pygmy's services. - fi - - docker exec -i $(docker-compose ps -q cli) bash -c "ssh-add -L|grep -vq 'ssh-rsa'" && error "SSH key was not added into container. Run 'pygmy restart'." - - curl -L -s -o /dev/null -w "%{http_code}" $LOCALDEV_URL | grep -q -v 200 && error "Unable to access $LOCALDEV_URL" && exit 1 - - if curl -L -s -N $LOCALDEV_URL | grep -q "name=\"Generator\" content=\"Drupal 8"; then - success "Successfully bootstrapped $LOCALDEV_URL" - else - error "Website is running, but cannot be bootstrapped. Try pulling latest container images with 'composer bay:pull'" && exit 1 - fi -} - -# -# Check that command exists. -# -command_exists() { - local cmd=$1 - command -v $cmd | grep -ohq $cmd - local res=$? - - # Try homebrew lookup. - if [ "$res" == "1" ] ; then - brew --prefix $cmd > /dev/null - res=$? - fi - - echo $res -} - -# -# Status echo. -# -status() { - cecho blue "==> $1"; -} - -# -# Success echo. -# -success() { - cecho green "✓ $1"; -} - -# -# Error echo. -# -error() { - cecho red "✘ $1"; -} - -# -# Colored echo. -# -cecho() { - local prefix="\033[" - local input_color=$1 - local message="$2" - - local color="" - case "$input_color" in - black | bk) color="${prefix}0;30m";; - red | r) color="${prefix}1;31m";; - green | g) color="${prefix}1;32m";; - yellow | y) color="${prefix}1;33m";; - blue | b) color="${prefix}1;34m";; - purple | p) color="${prefix}1;35m";; - cyan | c) color="${prefix}1;36m";; - gray | gr) color="${prefix}0;37m";; - *) message="$1" - esac - - # Format message with color codes, but only if a correct color was provided. - [ -n "$color" ] && message="${color}${message}${prefix}0m" - - echo -e "$message" -} - -main "$@" diff --git a/scripts/drupal-init-module.sh b/scripts/drupal-init-module.sh deleted file mode 100755 index 8d3f6b0e0..000000000 --- a/scripts/drupal-init-module.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/env bash -## -# Install current module. -# -# This script must run from within container. -# -set -e - -PACKAGE_NAME=${PACKAGE_NAME:-} -PACKAGE_ORG=${PACKAGE_ORG:-dpc-sdp} -LOCAL_REPOS=${LOCAL_REPOS:-$PACKAGE_ORG} -INSTALL_SUGGEST=${INSTALL_SUGGEST:-} -COMPOSER=${COMPOSER:-composer.build.json} -APP=${APP:-/app} -WEBROOT=${WEBROOT:-docroot} -TEST_PACKAGE_NAME=${TEST_PACKAGE_NAME:-tide_test} -TEST_PACKAGE_VERSION=${TEST_PACKAGE_VERSION:-^4.0} -URI=${LOCALDEV_URL:-dev} - -echo "==> COMPOSER_MEMORY_LIMIT=$COMPOSER_MEMORY_LIMIT" - -# Extract module name from the *.info.yml file, if not provided. -if [ "$PACKAGE_NAME" == "" ]; then - PACKAGE_NAME=$(find * -maxdepth 0 -name '*.info.yml'|cut -d. -f1) -fi -[ "$PACKAGE_NAME" == "" ] && "ERROR: Package name is not provided" && exit 1 - -if grep -q 'type: module' *.info.yml; then - echo "==> Started $PACKAGE_NAME module installation" -else - echo "Skipping module installation as current project is not a module" - exit 0 -fi - -# Require an additional test package. -[ "${PACKAGE_NAME}" != "${TEST_PACKAGE_NAME}" ] && composer require --prefer-source ${PACKAGE_ORG}/${TEST_PACKAGE_NAME}:${TEST_PACKAGE_VERSION} - -# Require module from local repository. -composer require --prefer-source ${PACKAGE_ORG}/${PACKAGE_NAME}:@dev - -# If running with suggested modules, install them first. -if [ "$INSTALL_SUGGEST" == "1" ] ; then - composer_suggests=$(cat ${COMPOSER} | gojq -r 'select(.suggest != null) | .suggest | keys[]') - for composer_suggest in $composer_suggests - do - echo "==> Requiring suggested module $composer_suggest" - composer require $composer_suggest - done - - drupal_suggests=$(cat ${COMPOSER} | gojq -r 'select(.suggest != null) | .suggest | keys[]' | sed "s/$PACKAGE_ORG\///" | cut -f1 -d":") - for drupal_suggest in $drupal_suggests - do - echo "==> Enabling suggested module $drupal_suggest" - drush -r ${APP}/${WEBROOT} --uri=${URI} en -y $drupal_suggest - done -fi - -drush -r ${APP}/${WEBROOT} --uri=${URI} en -y ${PACKAGE_NAME} - -echo "==> Finished $PACKAGE_NAME module installation" diff --git a/scripts/drupal-init-profile.sh b/scripts/drupal-init-profile.sh deleted file mode 100755 index 57543c603..000000000 --- a/scripts/drupal-init-profile.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash -## -# Install current profile. -# -# This script must run from within container. -# -set -e - -PACKAGE_NAME=${PACKAGE_NAME:-} -PACKAGE_ORG=${PACKAGE_ORG:-dpc-sdp} -LOCAL_REPOS=${LOCAL_REPOS:-$PACKAGE_ORG} -INSTALL_SUGGEST=${INSTALL_SUGGEST:-} -COMPOSER=${COMPOSER:-composer.build.json} -APP=${APP:-/app} -WEBROOT=${WEBROOT:-docroot} - -# Extract module name from the *.info.yml file, if not provided. -if [ "$PACKAGE_NAME" == "" ]; then - PACKAGE_NAME=$(find * -maxdepth 0 -name '*.info.yml'|cut -d. -f1) -fi -[ "$PACKAGE_NAME" == "" ] && "ERROR: Package name is not provided" && exit 1 - -echo "==> Started $PACKAGE_NAME profile installation" - -# Require module from local repository. -composer require --prefer-source ${PACKAGE_ORG}/${PACKAGE_NAME}:@dev - -# If running with suggested modules, install suggested modules first. -if [ "$INSTALL_SUGGEST" == "1" ] ; then - composer_suggests=$(cat ${COMPOSER} | gojq -r 'select(.suggest != null) | .suggest | keys[]') - for composer_suggest in $composer_suggests - do - echo "==> Requiring suggested module $composer_suggest" - composer require $composer_suggest - done -fi - -echo "==> Finished $PACKAGE_NAME profile installation" diff --git a/scripts/drupal/import-db.sh b/scripts/drupal/import-db.sh deleted file mode 100755 index beab2ffe7..000000000 --- a/scripts/drupal/import-db.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/sh -## -# Import prod db. -# - -# Clear cache if db found. -echo "==> Searching for Drupal installation" -[[ "$(drush core-status bootstrap --pipe)" != "" ]] && echo "==> Existing Drupal site found" && drush cr -echo "==> Removing existing database tables" -drush sql-drop -y -echo "==> Importing database" -drush sqlc < /tmp/.data/db.sql -drush sqlsan --sanitize-password=DpcFakePass --sanitize-email=user+%uid@localhost -y -# Ensure cache cleared -drush cr diff --git a/scripts/drupal/install-new-site.sh b/scripts/drupal/install-new-site.sh deleted file mode 100755 index 08d529239..000000000 --- a/scripts/drupal/install-new-site.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh - -## -# Install new Drupal site. -# - -DRUPAL_PROFILE=${DRUPAL_PROFILE:-testing} -DRUPAL_ADMIN_NAME=${DRUPAL_ADMIN_NAME:-admin} -DRUPAL_ADMIN_PASSWORD=${DRUPAL_ADMIN_PASSWORD:-admin} -APP=${APP:-/app} -WEBROOT=${WEBROOT:-docroot} -URI= ${LOCALDEV_URL:-dev} - -drush -r ${APP}/${WEBROOT} si ${DRUPAL_PROFILE} -y --uri=${URI} --account-name=${DRUPAL_ADMIN_NAME} --account-pass=${DRUPAL_ADMIN_PASSWORD} install_configure_form.enable_update_status_module=NULL install_configure_form.enable_update_status_emails=NULL --site-name="Single Digital Presence Content Management System" -drush -r ${APP}/${WEBROOT} ublk --uri=${URI} 1 -drush -r ${APP}/${WEBROOT} cr --uri=${URI} -y -drush -r ${APP}/${WEBROOT} --uri=${URI} php-eval "\$prefix = getenv('DRUPAL_MODULE_PREFIX'); if (\$prefix) { \$loaded = module_load_install(\$prefix . '_core'); if (\$loaded) { \$func = \$prefix . '_core_enable_modules'; \$func(TRUE); } }" diff --git a/scripts/drush-download-db.sh b/scripts/drush-download-db.sh deleted file mode 100755 index 1b9325b78..000000000 --- a/scripts/drush-download-db.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/sh -## -# Download latest DB backup via drush. -# - -LOCAL_PATH="/tmp/.data/" -DB_FILE_NAME="db.sql" -LOCAL_DB_PATH=$LOCAL_PATH$DB_FILE_NAME -DUMP_PATH="/tmp/$DB_FILE_NAME" - -fetch_db() { - mkdir -p $LOCAL_PATH - drush -y rsync @production:$DUMP_PATH $LOCAL_PATH > /dev/null - echo $? -} - -dump_db() { - drush @production sql-dump --skip-tables-key=common --result-file=$DUMP_PATH > /dev/null - echo $? -} - -if [ ! -f $LOCAL_DB_PATH ]; then - echo "Fetching production DB dump" - fetched=$(fetch_db) - - if [ "$fetched" != "0" ]; then - echo "Creating new production dump" - dumped=$(dump_db) - if [ "$dumped" != "0" ]; then - echo "Unable to dump DB file in production" - exit 1; - fi - - fetched=$(fetch_db) - if [ "$fetched" != "0" ] || [ ! -f $LOCAL_DB_PATH ]; then - echo "Unable to fetch DB dump" - exit 1; - fi - fi -fi - -[ ! -s $LOCAL_DB_PATH ] && rm -r $LOCAL_DB_PATH && "Invalid file size. Please check production DB" && exit 1 diff --git a/scripts/link-package.sh b/scripts/link-package.sh deleted file mode 100755 index 543dacb04..000000000 --- a/scripts/link-package.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bash -## -# Link current package. -# -# This script must run from within container. -# -set -e - -CUR_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) - -PACKAGE_NAME=${PACKAGE_NAME:-} -PACKAGE_ORG=${PACKAGE_ORG:-dpc-sdp} -LOCAL_REPOS=${LOCAL_REPOS:-$PACKAGE_ORG} - -# Extract module name from the *.info.yml file, if not provided. -if [ "$PACKAGE_NAME" == "" ]; then - PACKAGE_NAME=$(find * -maxdepth 0 -name '*.info.yml'|cut -d. -f1) -fi -[ "$PACKAGE_NAME" == "" ] && "ERROR: Package name is not provided" && exit 1 - -echo "==> Started $PACKAGE_NAME package linking" - -rm -Rf ${LOCAL_REPOS}/${PACKAGE_NAME} -mkdir -p ${LOCAL_REPOS}/${PACKAGE_NAME} -# Copy code at latest commit into local repository directory, so that it can be -# symlinked as a local package by composer. -git --git-dir=${CUR_DIR}/../.git --work-tree=${CUR_DIR}/.. ls-tree HEAD --name-only | xargs -I '{}' cp -R '{}' ${LOCAL_REPOS}/${PACKAGE_NAME}/ -cp -Rf ${CUR_DIR}/../.git ${LOCAL_REPOS}/${PACKAGE_NAME} - -# Add local package repository to composer configuration. It will by symlinked -# by default, rather then copied. -composer config repositories.${PACKAGE_ORG}/${PACKAGE_NAME} path ${LOCAL_REPOS}/${PACKAGE_NAME} - -echo "==> Finished $PACKAGE_NAME package linking" diff --git a/scripts/rebuild-env.sh b/scripts/rebuild-env.sh deleted file mode 100755 index 083cc9090..000000000 --- a/scripts/rebuild-env.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -## -# Rebuild environment. -# - -# Fetch prod db. -./scripts/drush-download-db.sh - -# Import db. -./scripts/drupal/import-db.sh - -# Deploy drupal. -./scripts/drupal/deploy.sh