From 5ddac5af9c870d8f72f5f2f7f0c52afd1bf14e83 Mon Sep 17 00:00:00 2001 From: nikathone Date: Mon, 24 Feb 2020 13:41:52 -0500 Subject: [PATCH 001/107] Added config files related to drupal, nginx, php and supervisord. --- config/drupal/confd/conf.d/app.env.toml | 3 + config/drupal/confd/templates/app.env.tmpl | 24 + config/nginx/conf.d/nginx.conf.toml | 3 + config/nginx/conf.d/vhost.conf.toml | 3 + config/nginx/templates/nginx.conf.tmpl | 44 ++ config/nginx/templates/vhost.conf.tmpl | 78 ++++ .../php/conf.d/docker-php-ext-apcu.ini.toml | 3 + .../conf.d/docker-php-ext-opcache.ini.toml | 3 + .../php/conf.d/docker-php-ext-xdebug.ini.toml | 3 + config/php/conf.d/docker-php.ini.toml | 3 + config/php/conf.d/zz-www.conf.toml | 3 + .../templates/docker-php-ext-apcu.ini.tmpl | 4 + .../templates/docker-php-ext-opcache.ini.tmpl | 9 + .../templates/docker-php-ext-xdebug.ini.tmpl | 26 ++ config/php/templates/docker-php.ini.tmpl | 422 ++++++++++++++++++ config/php/templates/zz-www.conf.tmpl | 70 +++ config/supervisord/nginx_php/supervisord.conf | 43 ++ 17 files changed, 744 insertions(+) create mode 100644 config/drupal/confd/conf.d/app.env.toml create mode 100644 config/drupal/confd/templates/app.env.tmpl create mode 100644 config/nginx/conf.d/nginx.conf.toml create mode 100644 config/nginx/conf.d/vhost.conf.toml create mode 100644 config/nginx/templates/nginx.conf.tmpl create mode 100644 config/nginx/templates/vhost.conf.tmpl create mode 100644 config/php/conf.d/docker-php-ext-apcu.ini.toml create mode 100644 config/php/conf.d/docker-php-ext-opcache.ini.toml create mode 100644 config/php/conf.d/docker-php-ext-xdebug.ini.toml create mode 100644 config/php/conf.d/docker-php.ini.toml create mode 100644 config/php/conf.d/zz-www.conf.toml create mode 100644 config/php/templates/docker-php-ext-apcu.ini.tmpl create mode 100644 config/php/templates/docker-php-ext-opcache.ini.tmpl create mode 100644 config/php/templates/docker-php-ext-xdebug.ini.tmpl create mode 100644 config/php/templates/docker-php.ini.tmpl create mode 100644 config/php/templates/zz-www.conf.tmpl create mode 100644 config/supervisord/nginx_php/supervisord.conf diff --git a/config/drupal/confd/conf.d/app.env.toml b/config/drupal/confd/conf.d/app.env.toml new file mode 100644 index 000000000..77413f359 --- /dev/null +++ b/config/drupal/confd/conf.d/app.env.toml @@ -0,0 +1,3 @@ +[template] +src = "app.env.tmpl" +dest = "/var/www/app/.env" diff --git a/config/drupal/confd/templates/app.env.tmpl b/config/drupal/confd/templates/app.env.tmpl new file mode 100644 index 000000000..3235d2261 --- /dev/null +++ b/config/drupal/confd/templates/app.env.tmpl @@ -0,0 +1,24 @@ +### APP SETTINGS + +APP_NAME={{ getenv "APP_NAME" "drupal" }} +APP_TITLE={{ getenv "APP_TITLE" "drupal" }} +APP_ACCOUNT_NAME={{ getenv "APP_ACCOUNT_NAME" "admin" }} +APP_ACCOUNT_MAIL={{ getenv "APP_ACCOUNT_MAIL" "drupal@example.com" }} +APP_ENV={{ getenv "APP_ENV" "local" }} + +DB_NAME={{ getenv "DB_NAME" "drupal" }} +DB_USER={{ getenv "DB_USER" "drupal" }} +DB_PASSWORD={{ getenv "DB_PASSWORD" "dbpassword" }} +DB_HOST={{ getenv "DB_HOST" "mariadb" }} +DB_DRIVER={{ getenv "DB_DRIVER" "mysql" }} +DB_PORT={{ getenv "DB_PORT" "3306" }} + +### Stack SETTINGS + +APP_ROOT={{ getenv "APP_ROOT" "/var/www/app" }} +APP_DOCROOT={{ getenv "APP_DOCROOT" "/var/www/app/web" }} +FILES_DIR={{ getenv "FILES_DIR" "/mnt/files" }} + +### Drush + +DRUSH_OPTIONS_URI={{ getenv "DRUSH_OPTIONS_URI" "" }} diff --git a/config/nginx/conf.d/nginx.conf.toml b/config/nginx/conf.d/nginx.conf.toml new file mode 100644 index 000000000..3e968ea8b --- /dev/null +++ b/config/nginx/conf.d/nginx.conf.toml @@ -0,0 +1,3 @@ +[template] +src = "nginx.conf.tmpl" +dest = "/etc/nginx/nginx.conf" diff --git a/config/nginx/conf.d/vhost.conf.toml b/config/nginx/conf.d/vhost.conf.toml new file mode 100644 index 000000000..f7d52405e --- /dev/null +++ b/config/nginx/conf.d/vhost.conf.toml @@ -0,0 +1,3 @@ +[template] +src = "vhost.conf.tmpl" +dest = "/etc/nginx/conf.d/default.conf" diff --git a/config/nginx/templates/nginx.conf.tmpl b/config/nginx/templates/nginx.conf.tmpl new file mode 100644 index 000000000..0e393fc8e --- /dev/null +++ b/config/nginx/templates/nginx.conf.tmpl @@ -0,0 +1,44 @@ +user {{ getenv "NGINX_USER" "www-data" }}; + +worker_processes {{ getenv "NGINX_WORKER_PROCESSES" "auto" }}; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections {{ getenv "NGINX_WORKER_CONNECTIONS" "1024" }}; + multi_accept {{ getenv "NGINX_MULTI_ACCEPT" "on" }}; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + proxy_temp_path /tmp/proxy_temp; + client_body_temp_path /tmp/client_temp; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + + server_names_hash_bucket_size {{ getenv "NGINX_SERVER_NAMES_HASH_BUCKET_SIZE" "64" }}; + + client_max_body_size {{ getenv "NGINX_CLIENT_MAX_BODY_SIZE" "64m" }}; + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile {{ getenv "NGINX_SENDFILE" "on" }}; + tcp_nopush {{ getenv "NGINX_TCP_NOPUSH" "on" }}; + tcp_nodelay {{ getenv "NGINX_TCP_NODELAY" "on" }}; + types_hash_max_size {{ getenv "NGINX_TYPES_HASH_MAX_SIZE" "2048" }}; + + keepalive_timeout {{ getenv "NGINX_KEEPALIVE_TIMEOUT" "75s" }}; + keepalive_requests {{ getenv "NGINX_KEEPALIVE_REQUESTS" "100" }}; + + server_tokens {{ getenv "NGINX_SERVER_TOKENS" "off" }}; + + gzip {{ getenv "NGINX_GZIP" "on" }}; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/config/nginx/templates/vhost.conf.tmpl b/config/nginx/templates/vhost.conf.tmpl new file mode 100644 index 000000000..c9ae55f05 --- /dev/null +++ b/config/nginx/templates/vhost.conf.tmpl @@ -0,0 +1,78 @@ +server { + listen {{ getenv "NGINX_LISTEN_PORT" "80" }} default_server{{ if getenv "NGINX_HTTP2" }} http2{{ end }}; + server_name {{ getenv "NGINX_SERVER_NAME" "default" }}; + + root {{ getenv "NGINX_SERVER_ROOT" "/var/www/app/web" }}; + index index.php index.html; + + location = /favicon.ico { + log_not_found off; + access_log {{ getenv "NGINX_STATIC_ACCESS_LOG" "off" }}; + } + + location = /robots.txt { + allow all; + log_not_found off; + access_log {{ getenv "NGINX_STATIC_ACCESS_LOG" "off" }}; + } + + location ~ \..*/.*\.php$ { + return 403; + } + + location ~ ^/sites/.*/private/ { + return 403; + } + + # Block access to scripts in site files directory + location ~ ^/sites/[^/]+/files/.*\.php$ { + deny all; + } + + # Allow "Well-Known URIs" as per RFC 5785 + location ~* ^/.well-known/ { + allow all; + } + + # Block access to "hidden" files and directories whose names begin with a + # period. This includes directories used by version control systems such + # as Subversion or Git to store control files. + location ~ (^|/)\. { + return 403; + } + + location / { + try_files $uri /index.php?$query_string; + } + + location @rewrite { + rewrite ^/(.*)$ /index.php?q=$1; + } + + location ~ '\.php$|^/update.php' { + fastcgi_split_path_info ^(.+?\.php)(|/.*)$; + include fastcgi_params; + # Block httpoxy attacks. See https://httpoxy.org/. + fastcgi_param HTTP_PROXY ""; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param QUERY_STRING $query_string; + fastcgi_intercept_errors {{ getenv "NGINX_FASTCGI_INTERCEPT_ERRORS" "on" }}; + fastcgi_pass unix:/var/run/php-fpm.sock; + } + + # Fighting with Styles? This little gem is amazing. + location ~ ^/sites/.*/files/styles/ { + try_files $uri @rewrite; + } + + # Handle private files through Drupal. Private file's path can come + # with a language prefix. + location ~ ^(/[a-z\-]+)?/system/files/ { + try_files $uri /index.php?$query_string; + } + + location ~* ^(?:.+\.(?:htaccess|make|txt|engine|inc|info|install|module|profile|po|pot|sh|.*sql|test|theme|tpl(?:\.php)?|xtmpl)|code-style\.pl|/Entries.*|/Repository|/Root|/Tag|/Template)$ { + return 404; + } +} diff --git a/config/php/conf.d/docker-php-ext-apcu.ini.toml b/config/php/conf.d/docker-php-ext-apcu.ini.toml new file mode 100644 index 000000000..9cabe33fe --- /dev/null +++ b/config/php/conf.d/docker-php-ext-apcu.ini.toml @@ -0,0 +1,3 @@ +[template] +src = "docker-php-ext-apcu.ini.tmpl" +dest = "/usr/local/etc/php/conf.d/docker-php-ext-apcu.ini" diff --git a/config/php/conf.d/docker-php-ext-opcache.ini.toml b/config/php/conf.d/docker-php-ext-opcache.ini.toml new file mode 100644 index 000000000..59c27c8ae --- /dev/null +++ b/config/php/conf.d/docker-php-ext-opcache.ini.toml @@ -0,0 +1,3 @@ +[template] +src = "docker-php-ext-opcache.ini.tmpl" +dest = "/usr/local/etc/php/conf.d/docker-php-ext-opcache.ini" diff --git a/config/php/conf.d/docker-php-ext-xdebug.ini.toml b/config/php/conf.d/docker-php-ext-xdebug.ini.toml new file mode 100644 index 000000000..f10c1d7f3 --- /dev/null +++ b/config/php/conf.d/docker-php-ext-xdebug.ini.toml @@ -0,0 +1,3 @@ +[template] +src = "docker-php-ext-xdebug.ini.tmpl" +dest = "/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini" diff --git a/config/php/conf.d/docker-php.ini.toml b/config/php/conf.d/docker-php.ini.toml new file mode 100644 index 000000000..22ab90dd7 --- /dev/null +++ b/config/php/conf.d/docker-php.ini.toml @@ -0,0 +1,3 @@ +[template] +src = "docker-php.ini.tmpl" +dest = "/usr/local/etc/php/conf.d/docker-php.ini" diff --git a/config/php/conf.d/zz-www.conf.toml b/config/php/conf.d/zz-www.conf.toml new file mode 100644 index 000000000..690939af5 --- /dev/null +++ b/config/php/conf.d/zz-www.conf.toml @@ -0,0 +1,3 @@ +[template] +src = "zz-www.conf.tmpl" +dest = "/usr/local/etc/php-fpm.d/zz-www.conf" diff --git a/config/php/templates/docker-php-ext-apcu.ini.tmpl b/config/php/templates/docker-php-ext-apcu.ini.tmpl new file mode 100644 index 000000000..f406016f1 --- /dev/null +++ b/config/php/templates/docker-php-ext-apcu.ini.tmpl @@ -0,0 +1,4 @@ +[apcu] +extension=apcu.so +apc.shm_size = {{ getenv "PHP_APCU_SHM_SIZE" "128M" }} +apc.enable_cli = {{ getenv "PHP_APCU_ENABLE_CLI" "Off" }} diff --git a/config/php/templates/docker-php-ext-opcache.ini.tmpl b/config/php/templates/docker-php-ext-opcache.ini.tmpl new file mode 100644 index 000000000..b132edbfa --- /dev/null +++ b/config/php/templates/docker-php-ext-opcache.ini.tmpl @@ -0,0 +1,9 @@ +[opcache] +zend_extension = opcache.so +opcache.enable = {{ getenv "PHP_OPCACHE_ENABLE" "On" }} +opcache.enable_cli = {{ getenv "PHP_OPCACHE_ENABLE_CLI" "Off" }} +opcache.memory_consumption = {{ getenv "PHP_OPCACHE_MEMORY_CONSUMPTION" "128" }} +opcache.interned_strings_buffer = {{ getenv "PHP_OPCACHE_INTERNED_STRINGS_BUFFER" "8" }} +opcache.max_accelerated_files = {{ getenv "PHP_OPCACHE_MAX_ACCELERATED_FILES" "4096" }} +opcache.validate_timestamps = {{ getenv "PHP_OPCACHE_VALIDATE_TIMESTAMPS" "On" }} +opcache.revalidate_freq = {{ getenv "PHP_OPCACHE_REVALIDATE_FREQ" "2" }} diff --git a/config/php/templates/docker-php-ext-xdebug.ini.tmpl b/config/php/templates/docker-php-ext-xdebug.ini.tmpl new file mode 100644 index 000000000..ab4692c38 --- /dev/null +++ b/config/php/templates/docker-php-ext-xdebug.ini.tmpl @@ -0,0 +1,26 @@ +{{ if getenv "PHP_XDEBUG" }} +[xdebug] +zend_extension = xdebug.so + +xdebug.coverage_enable = {{ getenv "PHP_XDEBUG_COVERAGE_ENABLE" "On" }} +xdebug.default_enable = {{ getenv "PHP_XDEBUG_DEFAULT_ENABLE" "On" }} + +xdebug.remote_enable = {{ getenv "PHP_XDEBUG_REMOTE_ENABLE" "On" }} +xdebug.remote_handler = {{ getenv "PHP_XDEBUG_REMOTE_HANDLER" "dbgp" }} +xdebug.remote_connect_back = {{ getenv "PHP_XDEBUG_REMOTE_CONNECT_BACK" "On" }} +xdebug.remote_host = {{ getenv "PHP_XDEBUG_REMOTE_HOST" "localhost" }} +xdebug.remote_port = {{ getenv "PHP_XDEBUG_REMOTE_PORT" "9000" }} +xdebug.remote_log = "{{ getenv "PHP_XDEBUG_REMOTE_LOG" "" }}" +xdebug.remote_autostart = {{ getenv "PHP_XDEBUG_REMOTE_AUTOSTART" "On" }} + +xdebug.profiler_enable = {{ getenv "PHP_XDEBUG_PROFILER_ENABLE" "Off" }} +xdebug.profiler_enable_trigger = {{ getenv "PHP_XDEBUG_PROFILER_ENABLE_TRIGGER" "On" }} +xdebug.profiler_enable_trigger_value = "{{ getenv "PHP_XDEBUG_PROFILER_ENABLE_TRIGGER_VALUE" "XDEBUG_PROFILE" }}" +xdebug.profiler_output_dir = {{ getenv "FILES_DIR" }}/xdebug/profiler +xdebug.profiler_output_name = {{ getenv "PHP_XDEBUG_PROFILER_OUTPUT_NAME" "cachegrind.out.%p" }} + +xdebug.idekey = {{ getenv "PHP_XDEBUG_IDEKEY" "PHPSTORM"}} + +xdebug.max_nesting_level = {{ getenv "PHP_XDEBUG_MAX_NESTING_LEVEL" "256" }} + +{{ end }} diff --git a/config/php/templates/docker-php.ini.tmpl b/config/php/templates/docker-php.ini.tmpl new file mode 100644 index 000000000..97f38521c --- /dev/null +++ b/config/php/templates/docker-php.ini.tmpl @@ -0,0 +1,422 @@ +; This file is used to override the default php.ini values +; BASIC SETTINGS: $PHP_INI_DIR/php.ini +; PHP-FPM SETTINGS: /usr/local/etc/php-fpm.d/zz-www.conf + +[PHP] + +;;;;;;;;;;;;;;;;;;;; +; Language Options ; +;;;;;;;;;;;;;;;;;;;; + +; Output buffering is a mechanism for controlling how much output data +; (excluding headers and cookies) PHP should keep internally before pushing that +; data to the client. If your application's output exceeds this setting, PHP +; will send that data in chunks of roughly the size you specify. +; Turning on this setting and managing its maximum buffer size can yield some +; interesting side-effects depending on your application and web server. +; You may be able to send headers and cookies after you've already sent output +; through print or echo. You also may see performance benefits if your server is +; emitting less packets due to buffered output versus PHP streaming the output +; as it gets it. On production servers, 4096 bytes is a good setting for performance +; reasons. +; Note: Output buffering can also be controlled via Output Buffering Control +; functions. +; Possible Values: +; On = Enabled and buffer is unlimited. (Use with caution) +; Off = Disabled +; Integer = Enables the buffer and sets its maximum size in bytes. +; Note: This directive is hardcoded to Off for the CLI SAPI +; Default Value: Off +; Development Value: 4096 +; Production Value: 4096 +; http://php.net/output-buffering +output_buffering = {{ getenv "PHP_OUTPUT_BUFFERING" "4096" }} + +; Duration of time, in seconds for which to cache realpath information for a given +; file or directory. For systems with rarely changing files, consider increasing this +; value. +; http://php.net/realpath-cache-ttl +realpath_cache_ttl = {{ getenv "PHP_REALPATH_CACHE_TTL" "120" }} + +; URL rewriter function rewrites URL on the fly by using +; output buffer. You can set target tags by this configuration. +; "form" tag is special tag. It will add hidden input tag to pass values. +; Refer to session.trans_sid_tags for usage. +; Default Value: "form=" +; Development Value: "form=" +; Production Value: "form=" +url_rewriter.tags = "{{ getenv "PHP_URL_REWRITER_TAGS" "a=href,area=href,frame=src,input=src,form=fakeentry" }}" + +;;;;;;;;;;;;;;;;; +; Miscellaneous ; +;;;;;;;;;;;;;;;;; + +; Decides whether PHP may expose the fact that it is installed on the server +; (e.g. by adding its signature to the Web server header). It is no security +; threat in any way, but it makes it possible to determine whether you use PHP +; on your server or not. +; http://php.net/expose-php +expose_php = {{ getenv "PHP_EXPOSE_PHP" "On" }} + +;;;;;;;;;;;;;;;;;;; +; Resource Limits ; +;;;;;;;;;;;;;;;;;;; + +; Maximum execution time of each script, in seconds +; http://php.net/max-execution-time +; Note: This directive is hardcoded to 0 for the CLI SAPI +max_execution_time = {{ getenv "PHP_MAX_EXECUTION_TIME" "120" }} + +; Maximum amount of time each script may spend parsing request data. It's a good +; idea to limit this time on productions servers in order to eliminate unexpectedly +; long running scripts. +; Note: This directive is hardcoded to -1 for the CLI SAPI +; Default Value: -1 (Unlimited) +; Development Value: 60 (60 seconds) +; Production Value: 60 (60 seconds) +; http://php.net/max-input-time +max_input_time = {{ getenv "PHP_MAX_INPUT_TIME" "60" }} + +; How many GET/POST/COOKIE input variables may be accepted +max_input_vars = {{ getenv "PHP_MAX_INPUT_VARS" "1000" }} + +; Maximum amount of memory a script may consume (512MB) +; http://php.net/memory-limit +memory_limit = {{ getenv "PHP_MEMORY_LIMIT" "512M" }} + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Error handling and logging ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; This directive informs PHP of which errors, warnings and notices you would like +; it to take action for. The recommended way of setting values for this +; directive is through the use of the error level constants and bitwise +; operators. The error level constants are below here for convenience as well as +; some common settings and their meanings. +; By default, PHP is set to take action on all errors, notices and warnings EXCEPT +; those related to E_NOTICE and E_STRICT, which together cover best practices and +; recommended coding standards in PHP. For performance reasons, this is the +; recommend error reporting setting. Your production server shouldn't be wasting +; resources complaining about best practices and coding standards. That's what +; development servers and development settings are for. +; Note: The php.ini-development file has this setting as E_ALL. This +; means it pretty much reports everything which is exactly what you want during +; development and early testing. +; +; Error Level Constants: +; E_ALL - All errors and warnings (includes E_STRICT as of PHP 5.4.0) +; E_ERROR - fatal run-time errors +; E_RECOVERABLE_ERROR - almost fatal run-time errors +; E_WARNING - run-time warnings (non-fatal errors) +; E_PARSE - compile-time parse errors +; E_NOTICE - run-time notices (these are warnings which often result +; from a bug in your code, but it's possible that it was +; intentional (e.g., using an uninitialized variable and +; relying on the fact it is automatically initialized to an +; empty string) +; E_STRICT - run-time notices, enable to have PHP suggest changes +; to your code which will ensure the best interoperability +; and forward compatibility of your code +; E_CORE_ERROR - fatal errors that occur during PHP's initial startup +; E_CORE_WARNING - warnings (non-fatal errors) that occur during PHP's +; initial startup +; E_COMPILE_ERROR - fatal compile-time errors +; E_COMPILE_WARNING - compile-time warnings (non-fatal errors) +; E_USER_ERROR - user-generated error message +; E_USER_WARNING - user-generated warning message +; E_USER_NOTICE - user-generated notice message +; E_DEPRECATED - warn about code that will not work in future versions +; of PHP +; E_USER_DEPRECATED - user-generated deprecation warnings +; +; Common Values: +; E_ALL (Show all errors, warnings and notices including coding standards.) +; E_ALL & ~E_NOTICE (Show all errors, except for notices) +; E_ALL & ~E_NOTICE & ~E_STRICT (Show all errors, except for notices and coding standards warnings.) +; E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR (Show only errors) +; Default Value: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED +; Development Value: E_ALL +; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT +; http://php.net/error-reporting +error_reporting = {{ getenv "PHP_ERROR_REPORTING" "E_ALL & ~E_DEPRECATED & ~E_STRICT" }} + +; This directive controls whether or not and where PHP will output errors, +; notices and warnings too. Error output is very useful during development, but +; it could be very dangerous in production environments. Depending on the code +; which is triggering the error, sensitive information could potentially leak +; out of your application such as database usernames and passwords or worse. +; For production environments, we recommend logging errors rather than +; sending them to STDOUT. +; Possible Values: +; Off = Do not display any errors +; stderr = Display errors to STDERR (affects only CGI/CLI binaries!) +; On or stdout = Display errors to STDOUT +; Default Value: On +; Development Value: On +; Production Value: Off +; http://php.net/display-errors +display_errors = {{ getenv "PHP_DISPLAY_ERRORS" "Off" }} + +; The display of errors which occur during PHP's startup sequence are handled +; separately from display_errors. PHP's default behavior is to suppress those +; errors from clients. Turning the display of startup errors on can be useful in +; debugging configuration problems. We strongly recommend you +; set this to 'off' for production servers. +; Default Value: Off +; Development Value: On +; Production Value: Off +; http://php.net/display-startup-errors +display_startup_errors = {{ getenv "PHP_DISPLAY_STARTUP_ERRORS" "Off" }} + +; Besides displaying errors, PHP can also log errors to locations such as a +; server-specific log, STDERR, or a location specified by the error_log +; directive found below. While errors should not be displayed on productions +; servers they should still be monitored and logging is a great way to do that. +; Default Value: Off +; Development Value: On +; Production Value: On +; http://php.net/log-errors +log_errors = {{ getenv "PHP_LOG_ERRORS" "On" }} + +; Set maximum length of log_errors. In error_log information about the source is +; added. The default is 1024 and 0 allows to not apply any maximum length at all. +; http://php.net/log-errors-max-len +log_errors_max_len = {{ getenv "PHP_LOG_ERRORS_MAX_LEN" "1024" }} + +;;;;;;;;;;;;;;;;; +; Data Handling ; +;;;;;;;;;;;;;;;;; + +; Maximum size of POST data that PHP will accept. +; Its value may be 0 to disable the limit. It is ignored if POST data reading +; is disabled through enable_post_data_reading. +; http://php.net/post-max-size +post_max_size = {{ getenv "PHP_POST_MAX_SIZE" "32M" }} + +; Automatically add files before PHP document. +; http://php.net/auto-prepend-file +auto_prepend_file = {{ getenv "PHP_AUTO_PREPEND_FILE" }} + +; Automatically add files after PHP document. +; http://php.net/auto-append-file +auto_append_file = {{ getenv "PHP_AUTO_APPEND_FILE" }} + +;;;;;;;;;;;;;;;;;;;;;;;;; +; Paths and Directories ; +;;;;;;;;;;;;;;;;;;;;;;;;; + +; Determines the size of the realpath cache to be used by PHP. This value should +; be increased on systems where PHP opens many files to reflect the quantity of +; the file operations performed. +; Note: if open_basedir is set, the cache is disabled +; http://php.net/realpath-cache-size +realpath_cache_size = {{ getenv "PHP_REALPATH_CACHE_SIZE" "800K" }} + +;;;;;;;;;;;;;;;; +; File Uploads ; +;;;;;;;;;;;;;;;; + +; Maximum allowed size for uploaded files. +; http://php.net/upload-max-filesize +upload_max_filesize = {{ getenv "PHP_UPLOAD_MAX_FILESIZE" "100M" }} + +; Maximum number of files that can be uploaded via a single request +max_file_uploads = {{ getenv "PHP_MAX_FILE_UPLOADS" "20" }} + +;;;;;;;;;;;;;;;;;; +; Fopen wrappers ; +;;;;;;;;;;;;;;;;;; + +; Whether to allow the treatment of URLs (like http:// or ftp://) as files. +; http://php.net/allow-url-fopen +allow_url_fopen = {{ getenv "PHP_ALLOW_URL_FOPEN" "On" }} + +; Default timeout for socket based streams (seconds) +; http://php.net/default-socket-timeout +default_socket_timeout = {{ getenv "PHP_DEFAULT_SOCKET_TIMEOUT" "60" }} + +;;;;;;;;;;;;;;;;;;; +; Module Settings ; +;;;;;;;;;;;;;;;;;;; + +[Date] + +; Defines the default timezone used by the date functions +; http://php.net/date.timezone +date.timezone = {{ getenv "PHP_DATE_TIMEZONE" "America/Toronto"}} + +[Pdo_mysql] + +; If mysqlnd is used: Number of cache slots for the internal result set cache +; http://php.net/pdo_mysql.cache_size +pdo_mysql.cache_size = {{ getenv "PHP_PDO_MYSQL_CACHE_SIZE" "2000" }} + +[mail function] + +; For Unix only. You may supply arguments as well (default: "sendmail -t -i") +; or /usr/sbin/sendmail -t -i +; http://php.net/sendmail-path +sendmail_path = {{ getenv "PHP_SENDMAIL_PATH" "/bin/true" }} + +; Add X-PHP-Originating-Script: that will include uid of the script followed by the filename +mail.add_x_header = {{ getenv "PHP_MAIL_ADD_X_HEADER" "On" }} + +[ODBC] + +; Handling of binary data. 0 means passthru, 1 return as is, 2 convert to char. +; See the documentation on odbc_binmode and odbc_longreadlen for an explanation +; of odbc.defaultlrl and odbc.defaultbinmode +; http://php.net/odbc.defaultbinmode +odbc.defaultbinmode = 1 + +[MySQLi] + +; If mysqlnd is used: Number of cache slots for the internal result set cache +; http://php.net/mysqli.cache_size +mysqli.cache_size = {{ getenv "PHP_MYSQLI_CACHE_SIZE" "2000" }} + +[Session] + +; Handler used to store/retrieve data. +; http://php.net/session.save-handler +session.save_handler = {{ getenv "PHP_SESSION_SAVE_HANDLER" "files" }} +; Argument passed to save_handler. In the case of files, this is the path +; where data files are stored. Note: Windows users have to change this +; variable in order to use PHP's session functions. +; +; The path can be defined as: +; +; session.save_path = "N;/path" +; +; where N is an integer. Instead of storing all the session files in +; /path, what this will do is use subdirectories N-levels deep, and +; store the session data in those directories. This is useful if +; your OS has problems with many files in one directory, and is +; a more efficient layout for servers that handle many sessions. +; +; NOTE 1: PHP will not create this directory structure automatically. +; You can use the script in the ext/session dir for that purpose. +; NOTE 2: See the section on garbage collection below if you choose to +; use subdirectories for session storage +; +; The file storage module creates files using mode 600 by default. +; You can change that by using +; +; session.save_path = "N;MODE;/path" +; +; where MODE is the octal representation of the mode. Note that this +; does not overwrite the process's umask. +; http://php.net/session.save-path +session.save_path = "{{ getenv "PHP_SESSION_SAVE_PATH" "/tmp" }}" +; Whether to use cookies. +; http://php.net/session.use-cookies +session.use_cookies = {{ getenv "PHP_SESSION_USE_COOKIES" "1" }} +; This option forces PHP to fetch and use a cookie for storing and maintaining +; the session id. We encourage this operation as it's very helpful in combating +; session hijacking when not specifying and managing your own session id. It is +; not the be-all and end-all of session hijacking defense, but it's a good start. +; http://php.net/session.use-only-cookies +session.use_only_cookies = {{ getenv "PHP_SESSION_USE_ONLY_COOKIES" "1" }} +; Name of the session (used as cookie name). +; http://php.net/session.name +session.name = {{ getenv "PHP_SESSION_NAME" "PHPSESSID" }} +; Initialize session on request startup. +; http://php.net/session.auto-start +session.auto_start = {{ getenv "PHP_SESSION_AUTO_START" "0" }} + +; Lifetime in seconds of cookie or, if 0, until browser is restarted. +; http://php.net/session.cookie-lifetime +session.cookie_lifetime = {{ getenv "PHP_SESSION_COOKIE_LIFETIME" "2000" }} +; The path for which the cookie is valid. +; http://php.net/session.cookie-path +session.cookie_path = {{ getenv "PHP_SESSION_COOKIE_PATH" "/" }} +; The domain for which the cookie is valid. +; http://php.net/session.cookie-domain +session.cookie_domain = {{ getenv "PHP_SESSION_COOKIE_DOMAIN" }} +; Whether or not to add the httpOnly flag to the cookie, which makes it inaccessible to browser scripting languages such as JavaScript. +; http://php.net/session.cookie-httponly +session.cookie_httponly = {{ getenv "PHP_SESSION_COOKIE_HTTPONLY" }} + +; Handler used to serialize data. php is the standard serializer of PHP. +; http://php.net/session.serialize-handler +session.serialize_handler = {{ getenv "PHP_SESSION_SERIALIZE_HANDLER" "php" }} + +; Defines the probability that the 'garbage collection' process is started +; on every session initialization. The probability is calculated by using +; gc_probability/gc_divisor. Where session.gc_probability is the numerator +; and gc_divisor is the denominator in the equation. Setting this value to 1 +; when the session.gc_divisor value is 100 will give you approximately a 1% chance +; the gc will run on any give request. +; Default Value: 1 +; Development Value: 1 +; Production Value: 1 +; http://php.net/session.gc-probability +session.gc_probability = {{ getenv "PHP_SESSION_GC_PROBABILITY" "1" }} +; Defines the probability that the 'garbage collection' process is started on every +; session initialization. The probability is calculated by using the following equation: +; gc_probability/gc_divisor. Where session.gc_probability is the numerator and +; session.gc_divisor is the denominator in the equation. Setting this value to 1 +; when the session.gc_divisor value is 100 will give you approximately a 1% chance +; the gc will run on any give request. Increasing this value to 1000 will give you +; a 0.1% chance the gc will run on any give request. For high volume production servers, +; this is a more efficient approach. +; Default Value: 100 +; Development Value: 1000 +; Production Value: 1000 +; http://php.net/session.gc-divisor +session.gc_divisor = {{ getenv "PHP_SESSION_GC_DIVISOR" "100" }} +; After this number of seconds, stored data will be seen as 'garbage' and +; cleaned up by the garbage collection process. +; http://php.net/session.gc-maxlifetime +session.gc_maxlifetime = {{ getenv "PHP_SESSION_GC_MAXLIFETIME" "1440" }} + +; Check HTTP Referer to invalidate externally stored URLs containing ids. +; HTTP_REFERER has to contain this substring for the session to be +; considered as valid. +; http://php.net/session.referer-check +session.referer_check = {{ getenv "PHP_SESSION_REFERER_CHECK" "" }} + +; Set to {nocache,private,public,} to determine HTTP caching aspects +; or leave this empty to avoid sending anti-caching headers. +; http://php.net/session.cache-limiter +session.cache_limiter = {{ getenv "PHP_SESSION_CACHE_LIMITER" "nocache" }} +; Document expires after n minutes. +; http://php.net/session.cache-expire +session.cache_expire = {{ getenv "PHP_SESSION_CACHE_EXPIRE" "180" }} + +; trans sid support is disabled by default. +; Use of trans sid may risk your users' security. +; Use this option with caution. +; - User may send URL contains active session ID +; to other person via. email/irc/etc. +; - URL that contains active session ID may be stored +; in publicly accessible computer. +; - User may access your site with the same session ID +; always using URL stored in browser's history or bookmarks. +; http://php.net/session.use-trans-sid +session.use_trans_sid = {{ getenv "PHP_SESSION_USE_TRANS_SID" "0" }} + +; Set session ID character length. This value could be between 22 to 256. +; Shorter length than default is supported only for compatibility reason. +; Users should use 32 or more chars. +; http://php.net/session.sid-length +; Default Value: 32 +; Development Value: 26 +; Production Value: 26 +session.sid_length = {{ getenv "PHP_SESSION_SID_LENGTH" "32" }} + +; Define how many bits are stored in each character when converting +; the binary hash data to something readable. +; Possible values: +; 4 (4 bits: 0-9, a-f) +; 5 (5 bits: 0-9, a-v) +; 6 (6 bits: 0-9, a-z, A-Z, "-", ",") +; Default Value: 4 +; Development Value: 5 +; Production Value: 5 +; http://php.net/session.hash-bits-per-character +session.sid_bits_per_character = {{ getenv "PHP_SESSION_SID_BITS_PER_CHARACTER" "5" }} + +[Assertion] +zend.assertions = {{ getenv "PHP_ZEND_ASSERTIONS" "1" }} +assert.active = {{ getenv "PHP_ASSERT_ACTIVE" "On" }} diff --git a/config/php/templates/zz-www.conf.tmpl b/config/php/templates/zz-www.conf.tmpl new file mode 100644 index 000000000..1432222b3 --- /dev/null +++ b/config/php/templates/zz-www.conf.tmpl @@ -0,0 +1,70 @@ +[www] + +; Unix user/group of processes +; Note: The user is mandatory. If the group is not set, the default user's group +; will be used. +user = {{ getenv "PHP_FPM_USER" "www-data" }} +group = {{ getenv "PHP_FPM_GROUP" "www-data" }} + +listen = /var/run/php-fpm.sock +listen.owner = {{ getenv "PHP_FPM_USER" "www-data" }} +listen.group = {{ getenv "PHP_FPM_GROUP" "www-data" }} + +; Redirect worker stdout and stderr into main error log. If not set, stdout and +; stderr will be redirected to /dev/null according to FastCGI specs. +; Note: on highloaded environement, this can cause some delay in the page +; process time (several ms). +; Default Value: no +clear_env = {{ getenv "PHP_FPM_CLEAR_ENV" "yes" }} + +; Choose how the process manager will control the number of child processes. +; Possible Values: +; static - a fixed number (pm.max_children) of child processes; +; dynamic - the number of child processes are set dynamically based on the +; following directives. With this process management, there will be +; always at least 1 children. +; pm.max_children - the maximum number of children that can +; be alive at the same time. +; pm.start_servers - the number of children created on startup. +; pm.min_spare_servers - the minimum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is less than this +; number then some children will be created. +; pm.max_spare_servers - the maximum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is greater than this +; number then some children will be killed. +; ondemand - no children are created at startup. Children will be forked when +; new requests will connect. The following parameter are used: +; pm.max_children - the maximum number of children that +; can be alive at the same time. +; pm.process_idle_timeout - The number of seconds after which +; an idle process will be killed. +; Note: This value is mandatory. +pm = {{ getenv "PHP_FPM_PM" "dynamic" }} +; The number of child processes to be created when pm is set to 'static' and the +; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. +; This value sets the limit on the number of simultaneous requests that will be +; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. +; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP +; CGI. The below defaults are based on a server without much resources. Don't +; forget to tweak pm.* to fit your needs. +; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' +; Note: This value is mandatory. Default docker is 5. +pm.max_children = {{ getenv "PHP_FPM_PM_MAX_CHILDREN" "20" }} +; The number of child processes created on startup. +; Note: Used only when pm is set to 'dynamic' +; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 +pm.start_servers = {{ getenv "PHP_FPM_PM_START_SERVERS" "5" }} +; The desired minimum number of idle server processes. +; Note: Used and Mandatory only when pm is set to 'dynamic' +pm.min_spare_servers = {{ getenv "PHP_FPM_PM_MIN_SPARE_SERVERS" "5" }} +; The desired maximum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.max_spare_servers = {{ getenv "PHP_FPM_PM_MAX_SPARE_SERVERS" "5" }} +; The number of requests each child process should execute before respawning. +; This can be useful to work around memory leaks in 3rd party libraries. For +; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. +; Default Value: 0 +pm.max_requests = {{ getenv "PHP_FPM_PM_MAX_REQUESTS" "500" }} diff --git a/config/supervisord/nginx_php/supervisord.conf b/config/supervisord/nginx_php/supervisord.conf new file mode 100644 index 000000000..2062b3792 --- /dev/null +++ b/config/supervisord/nginx_php/supervisord.conf @@ -0,0 +1,43 @@ +[supervisord] +user=%(ENV_APP_RUNNER_USER)s +nodaemon=true +logfile=/dev/null +logfile_maxbytes=0 +pidfile=/var/run/supervisord.pid +loglevel = INFO + +[unix_http_server] +file=/var/run/supervisor.sock +chmod=0700 +username=docker +password=docker + +[supervisorctl] +serverurl=unix:///var/run/supervisord.sock +username=docker +password=docker + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface + +[program:php-fpm] +command = /usr/local/sbin/php-fpm +autostart=true +autorestart=true +priority=5 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:nginx] +command=/usr/sbin/nginx -g "daemon off;" +autostart=true +autorestart=true +priority=10 +stdout_events_enabled=true +stderr_events_enabled=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 From 2889ded12ecbc73652f3095fbd402dbe8815fb33 Mon Sep 17 00:00:00 2001 From: nikathone Date: Mon, 24 Feb 2020 13:58:36 -0500 Subject: [PATCH 002/107] Added bin folder which contains needed script for the app/drupal container. --- scripts/app_bin/docker-webserver-entrypoint | 53 +++++++++++++++++ scripts/app_bin/files_link | 43 ++++++++++++++ scripts/app_bin/install_drupal | 63 +++++++++++++++++++++ scripts/app_bin/wait_for_database | 37 ++++++++++++ 4 files changed, 196 insertions(+) create mode 100644 scripts/app_bin/docker-webserver-entrypoint create mode 100644 scripts/app_bin/files_link create mode 100644 scripts/app_bin/install_drupal create mode 100644 scripts/app_bin/wait_for_database diff --git a/scripts/app_bin/docker-webserver-entrypoint b/scripts/app_bin/docker-webserver-entrypoint new file mode 100644 index 000000000..3a5548109 --- /dev/null +++ b/scripts/app_bin/docker-webserver-entrypoint @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +set -e + +if [[ -n "${DEBUG}" ]]; then + set -x +fi + +# usage: file_env VAR [DEFAULT] +# ie: file_env 'DB_PASSWORD' 'example' +# (will allow for "$DB_PASSWORD_FILE" to fill in the value of +# "$DB_PASSWORD" from a file, especially for Docker's secrets feature) +file_env() { + local var="$1" + local fileVar="${var}_FILE" + local def="${2:-}" + if [[ "${!var:-}" ]] && [[ "${!fileVar:-}" ]]; then + echo >&2 "error: both $var and $fileVar are set (but are exclusive)" + exit 1 + fi + local val="${def}" + if [[ "${!var:-}" ]]; then + val="${!var}" + elif [[ "${!fileVar:-}" ]]; then + val="$(< "${!fileVar}")" + fi + export "$var"="$val" + unset "$fileVar" +} + +if [[ -f "${APP_DOCROOT}/sites/default/settings.${APP_NAME}.php" ]]; then + file_env "APP_NAME" + file_env "APP_ACCOUNT_PASS" + file_env "DB_NAME" + file_env "DB_USER" + file_env "DB_PASSWORD" +fi + +# Make sure files folder is linked at the proper folder +/usr/local/bin/files_link "${APP_DOCROOT}/sites/default/files" + +# set custom config files for the drupal .env where the credentials are. +/usr/local/bin/confd -onetime -backend env + +# Delay container to start until the database server is ready. +/usr/local/bin/wait_for_database + +# Attempt to install drupal. +if [[ "${AUTO_INSTALL}" == "true" ]]; then + /usr/local/bin/install_drupal +fi + +exec "$@" diff --git a/scripts/app_bin/files_link b/scripts/app_bin/files_link new file mode 100644 index 000000000..bbd10f417 --- /dev/null +++ b/scripts/app_bin/files_link @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +set -e + +if [[ -n "${DEBUG}" ]]; then + set -x +fi + +app_public_dir=$1 + +# Add symlink from persistent files volume to application's storage public dir. +if [[ -n "${app_public_dir}" ]]; then + echo >&2 + echo >&2 "[INFO] Application's public storage directory specified, trying to symlink from files persistent volume" + echo >&2 + + if [[ -d "${app_public_dir}" ]]; then + if [[ ! -L "${app_public_dir}" ]]; then + if [[ "$(ls -A "${app_public_dir}")" ]]; then + echo >&2 + echo >&2 "[ERROR] Failed to symlink public storage directory to a persistent volume" + echo >&2 " Directory ${app_public_dir} must not exists or be empty" + echo >&2 " (use files import to migrate existing public files)" + echo >&2 + exit 1 + # If dir is not symlink and empty, remove it and link. + else + echo >&2 + echo >&2 "[INFO] Empty public storage dir detected: removing and symlinking '${app_public_dir}' -> ''${FILES_DIR}/public'" + echo >&2 + rm -rf "${app_public_dir}" + ln -sf "${FILES_DIR}/public" "${app_public_dir}" + fi + else + echo "Symlink already in place" + fi + else + echo >&2 + echo >&2 "[INFO] No public storage dir detected: just symlinking '${app_public_dir}' -> '${FILES_DIR}/public'" + echo >&2 + ln -sf "${FILES_DIR}/public" "${app_public_dir}" + fi +fi diff --git a/scripts/app_bin/install_drupal b/scripts/app_bin/install_drupal new file mode 100644 index 000000000..7de9198cf --- /dev/null +++ b/scripts/app_bin/install_drupal @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +set -e + +if [[ -n "${DEBUG}" ]]; then + set -x +fi + +if [[ "${APP_ENV}" == "dev" && ! -f "${APP_DOCROOT}/index.php" ]]; then + cd ${APP_ROOT} + composer install --classmap-authoritative +fi; + +# Ensure we are in the app web. +cd ${APP_DOCROOT} + +# Determine if site already installed +site_installed=$(${APP_ROOT}/vendor/bin/drush status bootstrap | grep -q Successful; echo "$?") + +if [[ -n "${site_installed}" && "${site_installed}" -eq 0 ]]; then + echo >&2 + echo >&2 '[INFO] Site already installed' + echo >&2 ' You might need to run ../vendor/bin/drush config-import if' + echo >&2 ' the database configurations are out of sync.' + echo >&2 +else + site_install_options=( + --yes + --verbose + --account-name=${APP_ACCOUNT_NAME} + --account-mail=${APP_ACCOUNT_MAIL} + --account-pass=${APP_ACCOUNT_PASS} + ) + if [[ "$(ls -A ${APP_ROOT}/config/sync)" && -f ${APP_ROOT}/config/sync/core.extension.yml ]]; then + echo >&2 + echo >&2 '[INFO] Installing site using existing configurations' + echo >&2 '[INFO] Switching to minimal profile to allow existing config to install.' + echo >&2 ' See https://www.drupal.org/node/2897299 for more information.' + echo >&2 + sed -i 's/standard:/minimal:/g' ${APP_ROOT}/config/sync/core.extension.yml + sed -i 's/profile: standard/profile: minimal/g' ${APP_ROOT}/config/sync/core.extension.yml + site_install_options+=( + --existing-config + minimal + ) + else + echo >&2 + echo >&2 '[INFO] Installing site from scratch' + echo >&2 + site_install_options+=( + --site-name=${APP_NAME} + --site-mail=${APP_ACCOUNT_MAIL} + standard + ) + fi + + if [[ -n "${DEBUG}" ]]; then + site_install_options+=( --debug ) + fi + + # Installing drupal + ${APP_ROOT}/vendor/bin/drush site:install "${site_install_options[@]}" +fi diff --git a/scripts/app_bin/wait_for_database b/scripts/app_bin/wait_for_database new file mode 100644 index 000000000..7f2da2cc2 --- /dev/null +++ b/scripts/app_bin/wait_for_database @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +set -e + +if [[ -n "${DEBUG}" ]]; then + set -x +fi + +function fail { + echo $1 >&2 + exit 1 +} + +function retry { + local n=1 + local max=5 + local delay=5 + while true; do + "$@" && break || { + if [[ $n -lt $max ]]; then + ((n++)) + echo "Command failed. Attempt $n/$max:" + sleep $delay; + else + fail "The command has failed after $n attempts." + fi + } + done +} + +# Check connection to the database and verify if the database is created +retry mysql --silent --quick --force \ + --host=${DB_HOST} \ + --port=${DB_PORT} \ + --user=${DB_USER} \ + --password=${DB_PASSWORD} \ + --execute "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='${DB_NAME}'" >/dev/null 2>&1 From 03219d6f5d1661e1d745554753bd05e1bfdb5452 Mon Sep 17 00:00:00 2001 From: nikathone Date: Tue, 25 Feb 2020 12:46:35 -0500 Subject: [PATCH 003/107] Added app/drupal dockerfile and created the base webserver dockerfile. --- drupal.Dockerfile | 191 ++++++++++++++++++++++++++++++ images/nginx-php/Dockerfile | 168 ++++++++++++++++++++++++++ images/nginx-php/supervisord.conf | 43 +++++++ 3 files changed, 402 insertions(+) create mode 100644 drupal.Dockerfile create mode 100644 images/nginx-php/Dockerfile create mode 100644 images/nginx-php/supervisord.conf diff --git a/drupal.Dockerfile b/drupal.Dockerfile new file mode 100644 index 000000000..bbab7e8db --- /dev/null +++ b/drupal.Dockerfile @@ -0,0 +1,191 @@ +ARG build_environment=prod +ARG code_dir=./code +ARG base_image_tag=latest +ARG composer_version=1.9.3 +ARG templates_dir=./config +ARG auto_install=false + +# +# Stage 1: PHP Dependencies +# +# @TODO handle scafolded files +FROM composer:${composer_version} as composer-build +ARG code_dir +ARG build_environment +ENV COMPOSER_INSTALL_FLAGS \ + --ansi \ + --no-suggest \ + --prefer-dist \ + --no-interaction \ + --ignore-platform-reqs + +ENV DRUPAL_COMPOSER_DIRECTORIES \ + web/core \ + web/modules/contrib \ + web/profiles/contrib \ + web/themes/contrib \ + web/libraries \ + drush/contrib + +WORKDIR /root/.ssh +RUN chmod 0600 /root/.ssh \ + && ssh-keyscan -t rsa bitbucket.org >> known_hosts \ + && ssh-keyscan -t rsa github.com >> known_hosts \ + # To speed up download. + && composer global require hirak/prestissimo "${COMPOSER_INSTALL_FLAGS}" + +WORKDIR /app + +COPY ${code_dir}/composer.json ${code_dir}/composer.lock ./ + +RUN set -eux; \ + flags="${COMPOSER_INSTALL_FLAGS}"; \ + if [ "$build_environment" == "prod" ]; then \ + flags="${COMPOSER_INSTALL_FLAGS} --no-dev"; \ + fi; \ + composer install $flags \ + # make dummy directory just in case no drupal contrib related dependencies was created. + && for dir in $DRUPAL_COMPOSER_DIRECTORIES; do \ + if [ ! -d $dir ]; then \ + mkdir -p $dir; \ + fi; \ + done; + +# +# Stage 2: Any node related dependencies can be build here. e.g. compile scss to css +# +# Example coming soon + +# +# Stage 3: The base app/drupal +# +FROM registry.gitlab.com/nikathone/drupal-docker-good-defaults/php-nginx:${base_image_tag} as base +ARG code_dir +ARG auto_intall +ARG templates_dir +ARG app_runner_user=drupal +ARG app_runner_user_id=1000 +ARG app_runner_group=drupal +ARG app_runner_group_id=1000 +ARG app_bin_dir=./scripts/app_bin + +ENV PATH=${PATH}:${APP_ROOT}/vendor/bin \ + PHP_EXPOSE_PHP=Off \ + PHP_FPM_USER=${app_runner_user} \ + PHP_FPM_GROUP=${app_runner_group} \ + NGINX_LISTEN_PORT=8080 \ + DEFAULT_USER=${app_runner_user} \ + APP_NAME=drupal \ + AUTO_INSTALL=${auto_install:-false} \ + APP_RUNNER_USER=${app_runner_user} \ + APP_RUNNER_USER_ID=${app_runner_user_id:-1000} \ + APP_RUNNER_GROUP=${app_runner_group} \ + APP_RUNNER_GROUP_ID=${app_runner_group_id:-1000} + +# Copy custom excutable scripts +COPY ${app_bin_dir} /usr/local/bin/ + +# Copy custom configuration files for PHP and NGINX +COPY ${templates_dir}/php/conf.d /etc/confd/conf.d +COPY ${templates_dir}/nginx/conf.d /etc/confd/conf.d +COPY ${templates_dir}/php/templates /etc/confd/templates +COPY ${templates_dir}/nginx/templates /etc/confd/templates + +# Make sure docker-webserver-entrypoint and other scripts are executable +RUN chmod -R +x /usr/local/bin/; \ + # apply custom configurations based on confd templates + /usr/local/bin/confd -onetime -backend env \ + # clean the content of confd so that the app can add it's templates later in the process + && rm -rf /etc/confd/* + +# Add and configure app runner user +RUN set -xe; \ + # Delete existing user/group if uid/gid occupied. + existing_group=$(getent group "${APP_RUNNER_GROUP_ID}" | cut -d: -f1); \ + if [ -n "${existing_group}" ]; then delgroup "${existing_group}"; fi; \ + existing_user=$(getent passwd "${APP_RUNNER_USER_ID}" | cut -d: -f1); \ + if [ -n "${existing_user}" ]; then deluser "${existing_user}"; fi; \ + \ + # Ensure app runner user/group exists + addgroup --system --gid ${APP_RUNNER_GROUP_ID} ${APP_RUNNER_GROUP}; \ + adduser --system --disabled-password --ingroup ${APP_RUNNER_GROUP} --shell /bin/bash --uid ${APP_RUNNER_USER_ID} ${APP_RUNNER_USER}; \ + usermod --append --groups ${NGINX_USER_GROUP} ${APP_RUNNER_USER} \ + # Other app runner user related configurations. See bin/config_app_runner_user + && config_app_runner_user \ + \ + # Make sure that files dir have proper permissions. + && mkdir -p ${FILES_DIR}/public; \ + mkdir -p ${FILES_DIR}/private; \ + # Ensure the files dir is owned by nginx user + chown -R ${NGINX_USER}:${NGINX_USER_GROUP} ${FILES_DIR} + +EXPOSE $NGINX_LISTEN_PORT +ENTRYPOINT [ "docker-webserver-entrypoint" ] +CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/conf.d/supervisord.conf"] + +WORKDIR ${APP_ROOT} + +# Copy entire code files. The .dockerignore file ensures that ignored paths are +# not copied from the host machine into the image. +COPY --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} ${code_dir} ./ +# Copy code and composer generated files and directories +# @TODO Copy scalfolded files. +COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/vendor ./vendor +COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/web/core ./web/core +COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/web/modules/contrib ./web/modules/contrib +COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/web/themes/contrib ./web/themes/contrib +COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/web/profiles/contrib ./web/profiles/contrib +COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/web/libraries ./web/libraries +COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/drush/contrib ./drush/contrib +# If stage 2 available genrated front end css and css artifacts files can be copied + +# Copy custom configuration files for the app/drupal. We run confd in the docker entrypoint for this. +COPY ${templates_dir}/drupal/confd /etc/confd + +# +# Stage 4: The production setup +# +FROM base AS prod +ENV APP_ENV=prod + +# Using the production php.ini +RUN mv ${PHP_INI_DIR}/php.ini-production ${PHP_INI_DIR}/php.ini + +USER ${APP_RUNNER_USER} + +# +# Stage 5: The dev setup +# +FROM base AS dev +ARG composer_version +ARG templates_dir +ARG PHP_XDEBUG +ARG PHP_XDEBUG_DEFAULT_ENABLE +ARG PHP_XDEBUG_REMOTE_CONNECT_BACK +ARG PHP_XDEBUG_REMOTE_HOST +ARG PHP_IDE_CONFIG + +ENV APP_ENV=dev \ + DEBUG=true + +# Install development tools. +RUN pecl install xdebug-2.7.1; \ + docker-php-ext-enable xdebug; \ + # Adding the dev php.ini + mv ${PHP_INI_DIR}/php.ini-development ${PHP_INI_DIR}/php.ini + +COPY ${templates_dir}/php/conf.d/docker-php-ext-xdebug.ini.toml /etc/confd/conf.d/docker-php-ext-xdebug.ini.toml +COPY ${templates_dir}/php/templates/docker-php-ext-xdebug.ini.tmpl /etc/confd/templates/docker-php-ext-xdebug.ini.tmpl + +# Apply xdebug configurations +RUN /usr/local/bin/confd -onetime -backend env \ + # Delete xdebug configuration template files + && rm /etc/confd/conf.d/docker-php-ext-xdebug.ini.toml /etc/confd/templates/docker-php-ext-xdebug.ini.tmpl + +# Copy composer binary from official Composer image. Notice we didn't need composer for prod stage. +# @TODO try to use composer_version here +COPY --from=composer:1.9.3 /usr/bin/composer /usr/bin/composer + +EXPOSE 9000 + +USER ${APP_RUNNER_USER} diff --git a/images/nginx-php/Dockerfile b/images/nginx-php/Dockerfile new file mode 100644 index 000000000..f473d958d --- /dev/null +++ b/images/nginx-php/Dockerfile @@ -0,0 +1,168 @@ +ARG php_version=7.2.27 +ARG php_pkg_release=fpm-buster + +FROM php:${php_version}-${php_pkg_release} + +ARG nginx_version=1.17.8 +ARG nginx_njs_version=0.3.8 +ARG nginx_pkg_release=1~buster + +ENV CONFD_VERSION=0.16.0 \ + CONFD_SHA256SUM="255d2559f3824dd64df059bdc533fd6b697c070db603c76aaf8d1d5e6b0cc334" \ + FILES_DIR="/mnt/files" \ + APP_ROOT="/var/www/app" \ + APP_DOCROOT="/var/www/app/web" \ + APP_RUNNER_USER=root + +# Install apt dependencies +# @TODO use specific versions of some of these to avoid version conflict or breaking +RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \ + # utilitities cmd + dos2unix rsync wget findutils \ + # for imap + libc-client2007e-dev libkrb5-dev \ + # for ssh client only + openssh-client \ + # for git + git \ + # for bz2 + bzip2 libbz2-dev \ + # for gd + # libfreetype6 libfreetype6-dev libpng-tools libjpeg-dev libgmp-dev libwebp-dev \ + libfreetype6 libfreetype6-dev libpng-tools libgmp-dev libwebp-dev \ + # For image optimization + jpegoptim optipng pngquant \ + # For imagick + imagemagick libmagickwand-dev \ + # For nginx cgi-fcgi + libfcgi0ldbl \ + # for intl + libicu-dev \ + # for mcrypt + libmcrypt-dev \ + # for ldap + libldap2-dev \ + # for zip + libzip-dev zip unzip \ + # for xslt + libxslt1-dev \ + # for postgres + libpq-dev \ + # for tidy + libtidy-dev \ + # for yaml + libyaml-dev \ + # for command like drush sqlc/sqlq which need a mysql client + mariadb-client \ + # for supervsisor (http://supervisord.org/) + supervisor; \ + # install confd + wget -O /usr/local/bin/confd "https://github.com/kelseyhightower/confd/releases/download/v${CONFD_VERSION}/confd-${CONFD_VERSION}-linux-amd64"; \ + chmod +x /usr/local/bin/confd; \ + # verify confd signature + sha256sum /usr/local/bin/confd | grep -q "${CONFD_SHA256SUM}"; \ + exit $?; \ + rm -r /var/lib/apt/lists/* + +# Install and enable php extensions using the helper script provided by the base +# image +RUN docker-php-ext-configure imap --with-kerberos --with-imap-ssl \ + && docker-php-ext-configure gd --with-gd --with-webp-dir \ + --with-freetype-dir=/usr/include/freetype2 \ + --with-jpeg-dir=/usr/include/ \ + --with-png-dir=/usr/include/ \ + && docker-php-ext-configure intl --enable-intl \ + && docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu \ + && docker-php-ext-configure zip --with-libzip \ + && docker-php-ext-install -j$(nproc) \ + bcmath \ + bz2 \ + calendar \ + exif \ + gd \ + gettext \ + gmp \ + imap \ + intl \ + ldap \ + mysqli \ + opcache \ + pcntl \ + pdo_mysql \ + pdo_pgsql \ + pgsql \ + soap \ + sockets \ + tidy \ + xmlrpc \ + xsl \ + zip \ + # pecl related extensions + && pecl install \ + apcu-5.1.17 \ + imagick-3.4.4 \ + mcrypt-1.0.1 \ + yaml-2.0.4 \ + && docker-php-ext-enable \ + apcu \ + imagick \ + mcrypt \ + yaml + +# install nginx (copied from official nginx Dockerfile https://github.com/nginxinc/docker-nginx/blob/c817e28dd68b6daa33265a8cb527b1c4cd723b59/mainline/buster/Dockerfile) +ENV NGINX_VERSION=${nginx_version} \ + NGINX_PKG_RELEASE=${nginx_pkg_release} \ + NJS_VERSION=${nginx_njs_version} \ + NGINX_USER=www-data \ + NGINX_USER_GROUP=www-data + +RUN set -x \ + # Ensure nginx user/group first, to be consistent throughout docker variants + && user_exists=$(id -u ${NGINX_USER} > /dev/null 2>&1; echo $?) \ + && if [ user_exists -eq 0 ]; then \ + addgroup --system --gid 101 ${NGINX_USER_GROUP}; \ + adduser --system --disabled-login \ + --ingroup ${NGINX_USER_GROUP} --no-create-home \ + --home /nonexistent --gecos "${NGINX_USER} user" \ + --shell /bin/false --uid 101 ${NGINX_USER}; \ + fi \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 \ + && \ + NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \ + found=''; \ + for server in \ + ha.pool.sks-keyservers.net \ + hkp://keyserver.ubuntu.com:80 \ + hkp://p80.pool.sks-keyservers.net:80 \ + pgp.mit.edu \ + ; do \ + echo "Fetching GPG key $NGINX_GPGKEY from $server"; \ + apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \ + done; \ + test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \ + apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ + nginx-module-xslt=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ + nginx-module-geoip=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ + nginx-module-image-filter=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ + nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-${NGINX_PKG_RELEASE} \ + " \ + # Arches officialy built by upstream + && echo "deb https://nginx.org/packages/mainline/debian/ buster nginx" >> /etc/apt/sources.list.d/nginx.list \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + $nginxPackages \ + gettext-base \ + && apt-get remove --purge --auto-remove -y \ + && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \ + # Create default place to store files generated by the app + && install -o ${NGINX_USER} -g ${NGINX_USER} -d "${FILES_DIR}/public" "${FILES_DIR}/private"; \ + chmod -R 775 "${FILES_DIR}" + +EXPOSE 80 443 + +# Copy custom supervisord configuration file +COPY ./supervisord.conf /etc/supervisor/conf.d/supervisord.conf +CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/conf.d/supervisord.conf"] diff --git a/images/nginx-php/supervisord.conf b/images/nginx-php/supervisord.conf new file mode 100644 index 000000000..2062b3792 --- /dev/null +++ b/images/nginx-php/supervisord.conf @@ -0,0 +1,43 @@ +[supervisord] +user=%(ENV_APP_RUNNER_USER)s +nodaemon=true +logfile=/dev/null +logfile_maxbytes=0 +pidfile=/var/run/supervisord.pid +loglevel = INFO + +[unix_http_server] +file=/var/run/supervisor.sock +chmod=0700 +username=docker +password=docker + +[supervisorctl] +serverurl=unix:///var/run/supervisord.sock +username=docker +password=docker + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface + +[program:php-fpm] +command = /usr/local/sbin/php-fpm +autostart=true +autorestart=true +priority=5 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:nginx] +command=/usr/sbin/nginx -g "daemon off;" +autostart=true +autorestart=true +priority=10 +stdout_events_enabled=true +stderr_events_enabled=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 From 02bfe96f496d23edb0aa6f2442331354a03d0056 Mon Sep 17 00:00:00 2001 From: nikathone Date: Tue, 25 Feb 2020 12:47:23 -0500 Subject: [PATCH 004/107] Moved supervisord config under nginx and php image. --- config/supervisord/nginx_php/supervisord.conf | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 config/supervisord/nginx_php/supervisord.conf diff --git a/config/supervisord/nginx_php/supervisord.conf b/config/supervisord/nginx_php/supervisord.conf deleted file mode 100644 index 2062b3792..000000000 --- a/config/supervisord/nginx_php/supervisord.conf +++ /dev/null @@ -1,43 +0,0 @@ -[supervisord] -user=%(ENV_APP_RUNNER_USER)s -nodaemon=true -logfile=/dev/null -logfile_maxbytes=0 -pidfile=/var/run/supervisord.pid -loglevel = INFO - -[unix_http_server] -file=/var/run/supervisor.sock -chmod=0700 -username=docker -password=docker - -[supervisorctl] -serverurl=unix:///var/run/supervisord.sock -username=docker -password=docker - -[rpcinterface:supervisor] -supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface - -[program:php-fpm] -command = /usr/local/sbin/php-fpm -autostart=true -autorestart=true -priority=5 -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 - -[program:nginx] -command=/usr/sbin/nginx -g "daemon off;" -autostart=true -autorestart=true -priority=10 -stdout_events_enabled=true -stderr_events_enabled=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 From 7038cba627c53d01884285b07ae9db2a4ccc50c9 Mon Sep 17 00:00:00 2001 From: nikathone Date: Tue, 25 Feb 2020 15:21:49 -0500 Subject: [PATCH 005/107] Made a first pass for the default drupal dockerfile and docker-compose file. --- .gitignore | 4 + docker-compose.yml | 189 +++++++++++++++++++++------------------------ drupal.Dockerfile | 6 +- 3 files changed, 98 insertions(+), 101 deletions(-) diff --git a/.gitignore b/.gitignore index e542c5243..b5a09df3b 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,10 @@ site .vscode config/proxy/acme.json +#=================== +# Drupal and Mariadb files persistant storage +*_files + #=================== ## Wodby proto specific codebase \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 5f35b8a5d..e15927c63 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,114 +1,100 @@ -version: '3.7' +version: '2.4' ## ISLE 8 Draft Prototype ## Feb, 2020 ## MVP 1 - Traefik, Solr, MySQL & Drupal (using Woodby images for Apache, Drupal and Solr) services: - - apache: - image: wodby/apache:$APACHE_TAG - container_name: "${PROJECT_NAME}_apache" - networks: - - internal - depends_on: - - php - environment: - APACHE_LOG_LEVEL: debug - APACHE_BACKEND_HOST: php - APACHE_VHOST_PRESET: php - APACHE_DOCUMENT_ROOT: /var/www/html/web - volumes: - - ./codebase:/var/www/html -## For macOS users (https://wodby.com/docs/stacks/drupal/local#docker-for-mac) -## - ./codebase:/var/www/html:cached # User-guided caching -## - docker-sync:/var/www/html # Docker-sync - labels: - - "traefik.http.routers.${PROJECT_NAME}_apache.rule=Host(`${PROJECT_BASE_URL}`)" - - mariadb: - ## Using for protoype database - image: wodby/mariadb:$MARIADB_TAG - container_name: "${PROJECT_NAME}_mariadb" - networks: - - internal - stop_grace_period: 30s + image: mariadb:10.3.22 environment: - MYSQL_ROOT_PASSWORD: $DB_ROOT_PASSWORD - MYSQL_DATABASE: $DB_NAME - MYSQL_USER: $DB_USER - MYSQL_PASSWORD: $DB_PASSWORD + MYSQL_DATABASE: ${DB_NAME:-drupal} + MYSQL_USER: ${DB_USER:-drupal} + MYSQL_PASSWORD: ${DB_PASSWORD:-dbpassword} + MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-dbpassword} volumes: - ## TO DO: How to allow for slow query log setup? - ## TO DO: Customization: Allow for additional tuning of MySQL. - mariadb-data:/var/lib/mysql - # - ./mariadb-init:/docker-entrypoint-initdb.d # Place init .sql file(s) here. if needed e.g. fedora. - - - php: - image: wodby/drupal-php:$PHP_TAG - container_name: "${PROJECT_NAME}_php" - networks: - - internal + healthcheck: + # https://dev.mysql.com/doc/refman/8.0/en/mysqladmin.html#command_mysqladmin_ping + test: ["CMD", "mysqladmin", "ping", "--silent"] + + drupal: + build: + context: ./ + dockerfile: ./drupal.Dockerfile + target: dev + args: + - code_dir=./codebase + - auto_install=true + - templates_dir=./config + - app_bin_dir=./scripts/app_bin + - base_image_tag=latest + - build_environment=dev + # XDEBUG confd build time args - you can set it to 0 turn it off. + - PHP_XDEBUG=${PHP_XDEBUG:-1} + - PHP_IDE_CONFIG="serverName=${PHP_IDE_CONFIG_SERVER_NAME:-islandora.localhost}" + - PHP_XDEBUG_REMOTE_HOST=${PHP_XDEBUG_REMOTE_HOST:-host.docker.internal} + #-PHP_XDEBUG_REMOTE_HOST: host.docker.internal # Docker 18.03+ Mac/Win + #-PHP_XDEBUG_REMOTE_HOST: 172.17.0.1 # Linux + #-PHP_XDEBUG_REMOTE_HOST: 10.254.254.254 # macOS, Docker < 18.03 + #-PHP_XDEBUG_REMOTE_HOST: 10.0.75.1 # Windows, Docker < 18.03 + - PHP_XDEBUG_DEFAULT_ENABLE=${PHP_XDEBUG_DEFAULT_ENABLE:-1} + - PHP_XDEBUG_REMOTE_CONNECT_BACK=${PHP_XDEBUG_REMOTE_CONNECT_BACK:-0} + # NGINX confd build time args + - NGINX_SERVER_ROOT=${APP_DOCROOT:-/var/www/app/web} + - NGINX_LISTEN_PORT=${NGINX_LISTEN_PORT:-8080} + depends_on: + mariadb: + condition: service_healthy env_file: - php.env environment: - # PHP_SENDMAIL_PATH: /usr/sbin/sendmail -t -i -S mailhog:1025 - # PHP_SENDMAIL_PATH: /usr/sbin/sendmail -t -i -S opensmtpd:25 - DB_HOST: $DB_HOST - DB_PORT: $DB_PORT - DB_USER: $DB_USER - DB_PASSWORD: $DB_PASSWORD - DB_NAME: $DB_NAME - DB_DRIVER: $DB_DRIVER - PHP_FPM_USER: wodby - PHP_FPM_GROUP: wodby - COLUMNS: 80 # Set 80 columns for docker exec -it. -## Read instructions at https://wodby.com/docs/stacks/php/local/#xdebug -# PHP_XDEBUG: 1 -# PHP_XDEBUG_DEFAULT_ENABLE: 1 -# PHP_XDEBUG_REMOTE_CONNECT_BACK: 0 -# PHP_IDE_CONFIG: serverName=my-ide -# PHP_XDEBUG_IDEKEY: "my-ide" -# PHP_XDEBUG_REMOTE_HOST: host.docker.internal # Docker 18.03+ Mac/Win -# PHP_XDEBUG_REMOTE_HOST: 172.17.0.1 # Linux -# PHP_XDEBUG_REMOTE_HOST: 10.254.254.254 # macOS, Docker < 18.03 -# PHP_XDEBUG_REMOTE_HOST: 10.0.75.1 # Windows, Docker < 18.03 -# PHP_XDEBUG_REMOTE_LOG: /tmp/php-xdebug.log - volumes: - - ./scripts/drupal:/scripts/drupal - - ./codebase:/var/www/html -## For macOS users (https://wodby.com/docs/stacks/drupal/local#docker-for-mac) -# - ./codebase:/var/www/html:cached # User-guided caching -# - docker-sync:/var/www/html # Docker-sync -## For XHProf and Xdebug profiler traces -# - files:/mnt/files - - - solr: - image: wodby/solr:$SOLR_TAG - container_name: "${PROJECT_NAME}_solr" - networks: - - internal - environment: - SOLR_DEFAULT_CONFIG_SET: $SOLR_CONFIG_SET - SOLR_HEAP: 1024m + APP_NAME: ${APP_NAME:-islandora} + APP_ROOT: ${APP_ROOT:-/var/www/app} + APP_ACCOUNT_NAME: ${DRUPAL_USER_NAME:-islandora} + APP_ACCOUNT_MAIL: ${DRUPAL_USER_EMAIL:-islandora@example.com} + DB_NAME: ${DB_NAME:-drupal} + DB_USER: ${DB_USER:-drupal} + DB_PASSWORD: ${DB_PASSWORD:-dbpassword} + DB_HOST: "mariadb" + DB_PORT: ${DB_PORT:-3306} + DB_DRIVER: ${DB_DRIVER:-mysql} + DRUSH_OPTIONS_URI: ${DRUSH_OPTIONS_URI} + NGINX_LISTEN_PORT: 8080 volumes: - - solr-data:/opt/solr/server/solr - # TO DO: Determine what type of container handling is needed + # set delegated mode here on docker for mac for faster disk I/O + - ./codebase:${APP_ROOT:-/var/www/app}:delegated + - ./drupal_files:${FILES_DIR:-/mnt/files} + # Anonymous volumes as a workaround to prevent empty or non existent host + # folders from accidentally getting mounted in container and overwrite the + # composer build stage files. + - ${APP_ROOT:-/var/www/app}/vendor + - ${APP_DOCROOT:-/var/www/app/web}/modules/contrib + - ${APP_DOCROOT:-/var/www/app/web}/themes/contrib + - ${APP_DOCROOT:-/var/www/app/web}/profiles/contrib + - ${APP_DOCROOT:-/var/www/app/web}/libraries labels: - - "traefik.http.routers.${PROJECT_NAME}_solr.rule=Host(`solr.${PROJECT_BASE_URL}`)" - + - "traefik.http.routers.${APP_NAME:-islandora}.rule=Host(`${PROJECT_BASE_URL:-islandora.localhost}`)" + - "traefik.http.services.${APP_NAME:-islandora}.loadbalancer.server.port=8080" + + # solr: + # image: wodby/solr:$SOLR_TAG + # container_name: "${PROJECT_NAME}_solr" + # networks: + # - internal + # environment: + # SOLR_DEFAULT_CONFIG_SET: $SOLR_CONFIG_SET + # SOLR_HEAP: 1024m + # volumes: + # - solr-data:/opt/solr/server/solr + # # TO DO: Determine what type of container handling is needed + # labels: + # - "traefik.http.routers.${PROJECT_NAME}_solr.rule=Host(`solr.${PROJECT_BASE_URL}`)" traefik: # review https://hub.docker.com/_/traefik image: traefik:2.1.3 - container_name: "${PROJECT_NAME}_traefik" command: --api.insecure=true --providers.docker - networks: - internal: - external: ports: - '80:80' #- "443:443" @@ -121,20 +107,25 @@ services: # SSL Choice 2: To use commercial SSLs - uncomment ONLY the line below. Add your SSL certs (.cert, .pem, .key) files to config/traefik/ssl-certs # - ./config/traefik/ssl-certs:/certs:ro - # Use Environment vaiables to pass in Traefik config; no traefik.yml required + # Use Environment variables to pass in Traefik config; no traefik.yml required # by providers # Alternative to a static configureation /etc/traefik/traefik.yml" # Pass in config via flags or environment variables # https://docs.traefik.io/getting-started/configuration-overview # Obsolete - "./config/traefik/traefik.local.yml:/etc/traefik/traefik.yml" - -networks: - internal: - external: - +# networks: +# internal: +# external: volumes: + # For local database storage persistance we bind mount a local folder. + # This folder needs to be created prior to docker-compose up or build. mariadb-data: - solr-data: - # isle-dc-postgres-data # Added to prototype but not currently used \ No newline at end of file + driver: local + driver_opts: + type: none + o: bind + device: ./mariadb_files + # solr-data: + # isle-dc-postgres-data # Added to prototype but not currently used \ No newline at end of file diff --git a/drupal.Dockerfile b/drupal.Dockerfile index bbab7e8db..ee5c4748f 100644 --- a/drupal.Dockerfile +++ b/drupal.Dockerfile @@ -1,5 +1,5 @@ ARG build_environment=prod -ARG code_dir=./code +ARG code_dir=./codebase ARG base_image_tag=latest ARG composer_version=1.9.3 ARG templates_dir=./config @@ -68,12 +68,14 @@ ARG app_runner_user_id=1000 ARG app_runner_group=drupal ARG app_runner_group_id=1000 ARG app_bin_dir=./scripts/app_bin +ARG NGINX_LISTEN_PORT=8080 +ARG NGINX_SERVER_ROOT ENV PATH=${PATH}:${APP_ROOT}/vendor/bin \ PHP_EXPOSE_PHP=Off \ PHP_FPM_USER=${app_runner_user} \ PHP_FPM_GROUP=${app_runner_group} \ - NGINX_LISTEN_PORT=8080 \ + NGINX_LISTEN_PORT=${NGINX_LISTEN_PORT} \ DEFAULT_USER=${app_runner_user} \ APP_NAME=drupal \ AUTO_INSTALL=${auto_install:-false} \ From 55dce1375f944964f514c0c75507dbf2dff6a682 Mon Sep 17 00:00:00 2001 From: nikathone Date: Tue, 25 Feb 2020 16:52:39 -0500 Subject: [PATCH 006/107] Adjust auto install flag. --- docker-compose.yml | 8 ++++---- drupal.Dockerfile | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index e15927c63..601d06475 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -59,8 +59,8 @@ services: DB_HOST: "mariadb" DB_PORT: ${DB_PORT:-3306} DB_DRIVER: ${DB_DRIVER:-mysql} - DRUSH_OPTIONS_URI: ${DRUSH_OPTIONS_URI} - NGINX_LISTEN_PORT: 8080 + DRUSH_OPTIONS_URI: ${PROJECT_BASE_URL:-islandora.localhost} + NGINX_LISTEN_PORT: "8080" volumes: # set delegated mode here on docker for mac for faster disk I/O - ./codebase:${APP_ROOT:-/var/www/app}:delegated @@ -96,7 +96,7 @@ services: image: traefik:2.1.3 command: --api.insecure=true --providers.docker ports: - - '80:80' + - '8000:80' #- "443:443" #- "8080:8080" volumes: @@ -126,6 +126,6 @@ volumes: driver_opts: type: none o: bind - device: ./mariadb_files + device: ${PWD}/mariadb_files # solr-data: # isle-dc-postgres-data # Added to prototype but not currently used \ No newline at end of file diff --git a/drupal.Dockerfile b/drupal.Dockerfile index ee5c4748f..499ff2f76 100644 --- a/drupal.Dockerfile +++ b/drupal.Dockerfile @@ -3,7 +3,7 @@ ARG code_dir=./codebase ARG base_image_tag=latest ARG composer_version=1.9.3 ARG templates_dir=./config -ARG auto_install=false +ARG auto_install # # Stage 1: PHP Dependencies From b5927f56aa4253bb679be96b018fff38ff0983e5 Mon Sep 17 00:00:00 2001 From: nikathone Date: Tue, 25 Feb 2020 17:16:35 -0500 Subject: [PATCH 007/107] Added a drupal local url env var. --- docker-compose.yml | 6 +++--- drupal.Dockerfile | 4 +--- php.env | 3 ++- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 601d06475..f087c7301 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,7 +25,6 @@ services: target: dev args: - code_dir=./codebase - - auto_install=true - templates_dir=./config - app_bin_dir=./scripts/app_bin - base_image_tag=latest @@ -59,8 +58,9 @@ services: DB_HOST: "mariadb" DB_PORT: ${DB_PORT:-3306} DB_DRIVER: ${DB_DRIVER:-mysql} - DRUSH_OPTIONS_URI: ${PROJECT_BASE_URL:-islandora.localhost} + DRUSH_OPTIONS_URI: ${DRUPAL_LOCAL_URL:-islandora.localhost} NGINX_LISTEN_PORT: "8080" + AUTO_INSTALL: "true" volumes: # set delegated mode here on docker for mac for faster disk I/O - ./codebase:${APP_ROOT:-/var/www/app}:delegated @@ -74,7 +74,7 @@ services: - ${APP_DOCROOT:-/var/www/app/web}/profiles/contrib - ${APP_DOCROOT:-/var/www/app/web}/libraries labels: - - "traefik.http.routers.${APP_NAME:-islandora}.rule=Host(`${PROJECT_BASE_URL:-islandora.localhost}`)" + - "traefik.http.routers.${APP_NAME:-islandora}.rule=Host(`${DRUPAL_LOCAL_URL:-islandora.localhost}`)" - "traefik.http.services.${APP_NAME:-islandora}.loadbalancer.server.port=8080" # solr: diff --git a/drupal.Dockerfile b/drupal.Dockerfile index 499ff2f76..980dd28d6 100644 --- a/drupal.Dockerfile +++ b/drupal.Dockerfile @@ -3,7 +3,6 @@ ARG code_dir=./codebase ARG base_image_tag=latest ARG composer_version=1.9.3 ARG templates_dir=./config -ARG auto_install # # Stage 1: PHP Dependencies @@ -61,7 +60,6 @@ RUN set -eux; \ # FROM registry.gitlab.com/nikathone/drupal-docker-good-defaults/php-nginx:${base_image_tag} as base ARG code_dir -ARG auto_intall ARG templates_dir ARG app_runner_user=drupal ARG app_runner_user_id=1000 @@ -78,7 +76,7 @@ ENV PATH=${PATH}:${APP_ROOT}/vendor/bin \ NGINX_LISTEN_PORT=${NGINX_LISTEN_PORT} \ DEFAULT_USER=${app_runner_user} \ APP_NAME=drupal \ - AUTO_INSTALL=${auto_install:-false} \ + AUTO_INSTALL=${AUTO_INSTALL:-false} \ APP_RUNNER_USER=${app_runner_user} \ APP_RUNNER_USER_ID=${app_runner_user_id:-1000} \ APP_RUNNER_GROUP=${app_runner_group} \ diff --git a/php.env b/php.env index e69f9e236..f7c558b4a 100644 --- a/php.env +++ b/php.env @@ -12,4 +12,5 @@ DRUPAL_USER=islandora DRUPAL_SITE_NAME=ISLE 8 Local DRUPAL_SITE_MAIL=admin@example.com DRUPAL_USER_PASSWORD=islandora -DRUPAL_USER_EMAIL=islandora@example.com \ No newline at end of file +DRUPAL_USER_EMAIL=islandora@example.com +DRUPAL_LOCAL_URL=islandora.localhost From ef68ea4e799bbccae8114f8ac051d886ccdde269 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 26 Feb 2020 16:34:39 -0500 Subject: [PATCH 008/107] Switch the auto install to false. --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index f087c7301..4a9a88ba7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -60,7 +60,7 @@ services: DB_DRIVER: ${DB_DRIVER:-mysql} DRUSH_OPTIONS_URI: ${DRUPAL_LOCAL_URL:-islandora.localhost} NGINX_LISTEN_PORT: "8080" - AUTO_INSTALL: "true" + AUTO_INSTALL: "false" volumes: # set delegated mode here on docker for mac for faster disk I/O - ./codebase:${APP_ROOT:-/var/www/app}:delegated @@ -128,4 +128,4 @@ volumes: o: bind device: ${PWD}/mariadb_files # solr-data: - # isle-dc-postgres-data # Added to prototype but not currently used \ No newline at end of file + # isle-dc-postgres-data # Added to prototype but not currently used From e59bdd7f231c399501f588579e63f65cb8f1ba38 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 26 Feb 2020 16:37:46 -0500 Subject: [PATCH 009/107] Making a force push for the config app runner script. --- scripts/app_bin/config_app_runner_user | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 scripts/app_bin/config_app_runner_user diff --git a/scripts/app_bin/config_app_runner_user b/scripts/app_bin/config_app_runner_user new file mode 100644 index 000000000..02c5b445f --- /dev/null +++ b/scripts/app_bin/config_app_runner_user @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +set -e + +if [[ -n "${DEBUG}" ]]; then + set -x +fi + +# Adjust permission so that the regular user own the default home dir +chown ${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /home +# Make sure that app runner user can add supervisord and php-fpm sockets into /var/run folder +chown ${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /var/run && chmod g+w /var/run + +if [[ "${DEFAULT_USER}" == "${APP_RUNNER_USER}" ]]; then + echo "[info] The \"user\" directive makes sense only if the master process runs with super-user privileges" + sed -i -e '/user/!b' -e '/'${NGINX_USER}'/!b' -e '/'${NGINX_USER}'/d' /etc/nginx/nginx.conf + + # Make sure that every php-fpm conf file in /usr/local/etc/php-fpm.d/ doesn't have user or group directive enabled + for php_fpm_conf in /usr/local/etc/php-fpm.d/*.conf; do + sed -i 's/^user = /;&/' ${php_fpm_conf} + sed -i 's/^group = /;&/' ${php_fpm_conf} + done +fi + +# Allow app runner user to write into nginx logs +chown -R root:${APP_RUNNER_GROUP} /var/log/nginx +chmod -R g+w /var/log/nginx + +# Override native user and use the "_www" one created in the image +uid=$(stat -c %u /srv) +gid=$(stat -c %g /srv) +sed -i -r "s/${APP_RUNNER_USER}:x:\d+:\d+:/${APP_RUNNER_GROUP}:x:$uid:$gid:/g" /etc/passwd +sed -i -r "s/${APP_RUNNER_GROUP}:x:\d+:/${APP_RUNNER_GROUP}:x:$gid:/g" /etc/group From 793e7fc577b13382ef79610622f869db8d0536fb Mon Sep 17 00:00:00 2001 From: nikathone Date: Fri, 28 Feb 2020 18:18:35 -0500 Subject: [PATCH 010/107] Setting back auto install to true and using data folder for drupal database and files folders. --- docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 4a9a88ba7..6db6982ec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -60,11 +60,11 @@ services: DB_DRIVER: ${DB_DRIVER:-mysql} DRUSH_OPTIONS_URI: ${DRUPAL_LOCAL_URL:-islandora.localhost} NGINX_LISTEN_PORT: "8080" - AUTO_INSTALL: "false" + AUTO_INSTALL: "true" volumes: # set delegated mode here on docker for mac for faster disk I/O - ./codebase:${APP_ROOT:-/var/www/app}:delegated - - ./drupal_files:${FILES_DIR:-/mnt/files} + - ./data/drupal/files:${FILES_DIR:-/mnt/files} # Anonymous volumes as a workaround to prevent empty or non existent host # folders from accidentally getting mounted in container and overwrite the # composer build stage files. @@ -126,6 +126,6 @@ volumes: driver_opts: type: none o: bind - device: ${PWD}/mariadb_files + device: ${PWD}/data/drupal/database # solr-data: # isle-dc-postgres-data # Added to prototype but not currently used From 3f43deff262d178260f2691d06a646c529f62306 Mon Sep 17 00:00:00 2001 From: nikathone Date: Fri, 28 Feb 2020 18:22:39 -0500 Subject: [PATCH 011/107] Checking for the settings.isle.php file instead of the app name specific. --- scripts/app_bin/docker-webserver-entrypoint | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/app_bin/docker-webserver-entrypoint b/scripts/app_bin/docker-webserver-entrypoint index 3a5548109..4dfa73189 100644 --- a/scripts/app_bin/docker-webserver-entrypoint +++ b/scripts/app_bin/docker-webserver-entrypoint @@ -28,7 +28,7 @@ file_env() { unset "$fileVar" } -if [[ -f "${APP_DOCROOT}/sites/default/settings.${APP_NAME}.php" ]]; then +if [[ -f "${APP_DOCROOT}/sites/default/settings.isle.php" ]]; then file_env "APP_NAME" file_env "APP_ACCOUNT_PASS" file_env "DB_NAME" From 307f8bd7d84474b17a4cba1c91611afa0d89697b Mon Sep 17 00:00:00 2001 From: nikathone Date: Fri, 28 Feb 2020 18:23:33 -0500 Subject: [PATCH 012/107] Added a script to init drupal codebase and setup settings.php --- Makefile | 48 ++++++++++++++ config/drupal/settings.isle.php | 42 ++++++++++++ config/drupal/snippet.txt | 7 ++ scripts/drupal/init.sh | 114 ++++++++++++++++++++++++++++++++ 4 files changed, 211 insertions(+) create mode 100644 Makefile create mode 100644 config/drupal/settings.isle.php create mode 100644 config/drupal/snippet.txt create mode 100755 scripts/drupal/init.sh diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..b5f532419 --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +# The variable used to determine which composer create project to use. +# Run make drupal_init_help for more information +isle_codebase ?= islandora + +docker_compose_project ?= islandora + +.PHONY: help drupal_init up build down down_rmi_all down_rmi_local drupal_clean clean_local clean + +default: drupal_init up + +help: + ./scripts/drupal/init.sh --help + +drupal_init: + ./scripts/drupal/init.sh --codebase $(isle_codebase) + +up: + docker-compose -p $(docker_compose_project) up --remove-orphans --detach + +build: + docker-compose -p $(docker_compose_project) up \ + --build \ + --detach \ + --remove-orphans + +down: + docker-compose -p $(docker_compose_project) down --remove-orphans + +down_rmi_all: + docker-compose -p $(docker_compose_project) down \ + --rmi all \ + --volumes \ + --remove-orphans + +down_rmi_local: + docker-compose -p $(docker_compose_project) down \ + --rmi local \ + --volumes \ + --remove-orphans + +# @todo we might chmod here since drupal set settings.php to readonly after +# install +drupal_clean: + chmod u+w codebase/web/sites/default && rm -rf codebase data/drupal + +clean_local: down_rmi_local drupal_clean + +clean: down_rmi_all drupal_clean diff --git a/config/drupal/settings.isle.php b/config/drupal/settings.isle.php new file mode 100644 index 000000000..f99ff7e5f --- /dev/null +++ b/config/drupal/settings.isle.php @@ -0,0 +1,42 @@ + getenv('DB_NAME'), + 'username' => getenv('DB_USER'), + 'password' => getenv('DB_PASSWORD'), + 'host' => getenv('DB_HOST'), + 'port' => getenv('DB_PORT'), + 'driver' => 'mysql', + 'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', + 'prefix' => '', + 'collation' => 'utf8mb4_general_ci', +]; + +/** + * Files path settings. + */ +$settings['file_public_path'] = 'sites/default/files'; +$settings['file_private_path'] = getenv('FILES_DIR') . '/private'; +$settings['file_temporary_path'] = '/tmp'; + +/** + * Hash salt setting. + */ +if (empty($settings['hash_salt'])) { + $settings['hash_salt'] = getenv('DRUPAL_HASH_SALT', true) ?: getenv('DRUPAL_HASH_SALT'); +} + +/** + * Config directory setting. + */ +$settings['config_sync_directory'] = '../config/sync'; diff --git a/config/drupal/snippet.txt b/config/drupal/snippet.txt new file mode 100644 index 000000000..0f1282a7a --- /dev/null +++ b/config/drupal/snippet.txt @@ -0,0 +1,7 @@ + +/** + * Load isle override configuration, if available. + */ +if (file_exists($app_root . '/' . $site_path . '/settings.isle.php')) { + include $app_root . '/' . $site_path . '/settings.isle.php'; +} diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh new file mode 100755 index 000000000..b0b79d28e --- /dev/null +++ b/scripts/drupal/init.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash + +set -e + +# Print the number of arguments. +# echo "$#" + +codebase="drupal" +config_dir="$PWD/config/drupal" +scripts_dir="$PWD/scripts/drupal" + +function fail { + echo -e "\033[31m[ERROR]\033[0m $1" >&2 + exit 1 +} + +function help() { + echo "This command create the codebase folder using composer create-project based on drupal/recommended-project or islandora/drupal-project" + echo " " + echo "options:" + echo "-h, --help show brief help" + echo "-c, --codebase CODEBASE specify a codebase to use. drupal or islandora are the valid option" + exit 0 +} + +function install_drupal() { + local composer=$(command -v composer) + local args="create-project drupal/recommended-project:^8.8 codebase" + local flags="--ignore-platform-reqs --no-interaction" + local codebase="$1" + local drush_require="require drush/drush $flags" + + if [[ "$codebase" == "islandora" ]]; then + local args="create-project islandora/drupal-project codebase" + fi + + echo -e "\033[1m[INFO]\033[0m Installing drupal using composer" + echo " " + if [[ composer ]]; then + echo " You have composer installed locally and we are using it to download the drupal project." + echo >&2 + $composer $args $flags + echo " Adding drush to the drupal repository." + echo >&2 + cd codebase && $composer $drush_require && cd .. + elif [[ ! composer ]]; then + echo " Using the official composer docker image to download drupal." + mkdir -p $HOME/.composer && docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3 $composer_cmd_arguments $composer_cmd_flags + echo " Adding drush to the drupal repository." + echo >&2 + cd codebase && docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3 $drush_require && cd .. + else + fail "We could not download drupal. Please check your arguments and try again." + fi +} + +function setup_settings_isle_php() { + local default_dir="$PWD/codebase/web/sites/default" + local settings="${default_dir}/settings.php" + local settings_default="${default_dir}/default.settings.php" + local settings_isle="settings.isle.php" + local insert_after="\$settings\[\'entity_update_backup\'\] \= TRUE\;" + local snippet="${config_dir}/snippet.txt" + + if [[ -f "${default_dir}/${settings_isle}" ]]; then + fail "An existing settings.isle.php was found under ${default_dir}." + fi + + echo -e "\033[1m[INFO]\033[0m Setting up settings.isle.php to be included in settings.php" + echo " " + if [[ ! -f "${settings}" ]]; then + echo " An existing settings.php file couldn't be located and one is created from default.settings.php." + cp ${settings_default} ${settings} + else + echo " An existing settings.php file was found." + fi + + sed -i '' -e "/${insert_after}/r ${snippet}" ${settings} + cp "${config_dir}/${settings_isle}" "${default_dir}/${settings_isle}" + echo " settings.isle.php was successfully setup ." + echo >&2 +} + +while [ ! $# -eq 0 ] +do + case "$1" in + --help | -h) + help + exit + ;; + --debug | -d) + set -x + ;; + --codebase | -c) + shift + codebase=$1 + if [[ "$codebase" != 'drupal' && "$codebase" != 'islandora' ]]; then + fail "codebase:$codebase is unsupported. Only vanilla drupal or islandora can be downloaded." + fi + ;; + esac + shift +done + +# Checking if the codebase directory exists and quit. +if [[ -d "$PWD/codebase" && "$(ls -A $PWD/codebase)" ]]; then + fail "The codebase directory exists and is not empty. Please delete it and run this command again." +fi + +# Initialize drupal database and files persistent storage folders. +mkdir -p $PWD/data/drupal/files/public $PWD/data/drupal/files/private $PWD/data/drupal/database + +install_drupal $codebase +setup_settings_isle_php From 82f308ba5d94f0a9baff4140ea7f89255800373e Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 15:30:58 -0400 Subject: [PATCH 013/107] Removed drupal, php and nginx config confd templates since they will be now copied from the base image. --- config/drupal/confd/conf.d/app.env.toml | 3 - config/drupal/confd/templates/app.env.tmpl | 24 - config/drupal/load.environment.php | 15 + config/drupal/settings.isle.php | 4 +- config/nginx/conf.d/nginx.conf.toml | 3 - config/nginx/conf.d/vhost.conf.toml | 3 - config/nginx/templates/nginx.conf.tmpl | 44 -- config/nginx/templates/vhost.conf.tmpl | 78 ---- .../php/conf.d/docker-php-ext-apcu.ini.toml | 3 - .../conf.d/docker-php-ext-opcache.ini.toml | 3 - .../php/conf.d/docker-php-ext-xdebug.ini.toml | 3 - config/php/conf.d/docker-php.ini.toml | 3 - config/php/conf.d/zz-www.conf.toml | 3 - .../templates/docker-php-ext-apcu.ini.tmpl | 4 - .../templates/docker-php-ext-opcache.ini.tmpl | 9 - .../templates/docker-php-ext-xdebug.ini.tmpl | 26 -- config/php/templates/docker-php.ini.tmpl | 422 ------------------ config/php/templates/zz-www.conf.tmpl | 70 --- 18 files changed, 17 insertions(+), 703 deletions(-) delete mode 100644 config/drupal/confd/conf.d/app.env.toml delete mode 100644 config/drupal/confd/templates/app.env.tmpl create mode 100644 config/drupal/load.environment.php delete mode 100644 config/nginx/conf.d/nginx.conf.toml delete mode 100644 config/nginx/conf.d/vhost.conf.toml delete mode 100644 config/nginx/templates/nginx.conf.tmpl delete mode 100644 config/nginx/templates/vhost.conf.tmpl delete mode 100644 config/php/conf.d/docker-php-ext-apcu.ini.toml delete mode 100644 config/php/conf.d/docker-php-ext-opcache.ini.toml delete mode 100644 config/php/conf.d/docker-php-ext-xdebug.ini.toml delete mode 100644 config/php/conf.d/docker-php.ini.toml delete mode 100644 config/php/conf.d/zz-www.conf.toml delete mode 100644 config/php/templates/docker-php-ext-apcu.ini.tmpl delete mode 100644 config/php/templates/docker-php-ext-opcache.ini.tmpl delete mode 100644 config/php/templates/docker-php-ext-xdebug.ini.tmpl delete mode 100644 config/php/templates/docker-php.ini.tmpl delete mode 100644 config/php/templates/zz-www.conf.tmpl diff --git a/config/drupal/confd/conf.d/app.env.toml b/config/drupal/confd/conf.d/app.env.toml deleted file mode 100644 index 77413f359..000000000 --- a/config/drupal/confd/conf.d/app.env.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "app.env.tmpl" -dest = "/var/www/app/.env" diff --git a/config/drupal/confd/templates/app.env.tmpl b/config/drupal/confd/templates/app.env.tmpl deleted file mode 100644 index 3235d2261..000000000 --- a/config/drupal/confd/templates/app.env.tmpl +++ /dev/null @@ -1,24 +0,0 @@ -### APP SETTINGS - -APP_NAME={{ getenv "APP_NAME" "drupal" }} -APP_TITLE={{ getenv "APP_TITLE" "drupal" }} -APP_ACCOUNT_NAME={{ getenv "APP_ACCOUNT_NAME" "admin" }} -APP_ACCOUNT_MAIL={{ getenv "APP_ACCOUNT_MAIL" "drupal@example.com" }} -APP_ENV={{ getenv "APP_ENV" "local" }} - -DB_NAME={{ getenv "DB_NAME" "drupal" }} -DB_USER={{ getenv "DB_USER" "drupal" }} -DB_PASSWORD={{ getenv "DB_PASSWORD" "dbpassword" }} -DB_HOST={{ getenv "DB_HOST" "mariadb" }} -DB_DRIVER={{ getenv "DB_DRIVER" "mysql" }} -DB_PORT={{ getenv "DB_PORT" "3306" }} - -### Stack SETTINGS - -APP_ROOT={{ getenv "APP_ROOT" "/var/www/app" }} -APP_DOCROOT={{ getenv "APP_DOCROOT" "/var/www/app/web" }} -FILES_DIR={{ getenv "FILES_DIR" "/mnt/files" }} - -### Drush - -DRUSH_OPTIONS_URI={{ getenv "DRUSH_OPTIONS_URI" "" }} diff --git a/config/drupal/load.environment.php b/config/drupal/load.environment.php new file mode 100644 index 000000000..eef1d28d0 --- /dev/null +++ b/config/drupal/load.environment.php @@ -0,0 +1,15 @@ +safeLoad(); diff --git a/config/drupal/settings.isle.php b/config/drupal/settings.isle.php index f99ff7e5f..7fca57106 100644 --- a/config/drupal/settings.isle.php +++ b/config/drupal/settings.isle.php @@ -4,7 +4,7 @@ /** * @file - * Islandora settings override for drupal 8. + * Isle settings override for drupal 8. */ /** @@ -39,4 +39,4 @@ /** * Config directory setting. */ -$settings['config_sync_directory'] = '../config/sync'; +// $settings['config_sync_directory'] = '../config/sync'; diff --git a/config/nginx/conf.d/nginx.conf.toml b/config/nginx/conf.d/nginx.conf.toml deleted file mode 100644 index 3e968ea8b..000000000 --- a/config/nginx/conf.d/nginx.conf.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "nginx.conf.tmpl" -dest = "/etc/nginx/nginx.conf" diff --git a/config/nginx/conf.d/vhost.conf.toml b/config/nginx/conf.d/vhost.conf.toml deleted file mode 100644 index f7d52405e..000000000 --- a/config/nginx/conf.d/vhost.conf.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "vhost.conf.tmpl" -dest = "/etc/nginx/conf.d/default.conf" diff --git a/config/nginx/templates/nginx.conf.tmpl b/config/nginx/templates/nginx.conf.tmpl deleted file mode 100644 index 0e393fc8e..000000000 --- a/config/nginx/templates/nginx.conf.tmpl +++ /dev/null @@ -1,44 +0,0 @@ -user {{ getenv "NGINX_USER" "www-data" }}; - -worker_processes {{ getenv "NGINX_WORKER_PROCESSES" "auto" }}; -error_log /var/log/nginx/error.log warn; -pid /var/run/nginx.pid; - -events { - worker_connections {{ getenv "NGINX_WORKER_CONNECTIONS" "1024" }}; - multi_accept {{ getenv "NGINX_MULTI_ACCEPT" "on" }}; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - proxy_temp_path /tmp/proxy_temp; - client_body_temp_path /tmp/client_temp; - fastcgi_temp_path /tmp/fastcgi_temp; - uwsgi_temp_path /tmp/uwsgi_temp; - scgi_temp_path /tmp/scgi_temp; - - server_names_hash_bucket_size {{ getenv "NGINX_SERVER_NAMES_HASH_BUCKET_SIZE" "64" }}; - - client_max_body_size {{ getenv "NGINX_CLIENT_MAX_BODY_SIZE" "64m" }}; - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /var/log/nginx/access.log main; - - sendfile {{ getenv "NGINX_SENDFILE" "on" }}; - tcp_nopush {{ getenv "NGINX_TCP_NOPUSH" "on" }}; - tcp_nodelay {{ getenv "NGINX_TCP_NODELAY" "on" }}; - types_hash_max_size {{ getenv "NGINX_TYPES_HASH_MAX_SIZE" "2048" }}; - - keepalive_timeout {{ getenv "NGINX_KEEPALIVE_TIMEOUT" "75s" }}; - keepalive_requests {{ getenv "NGINX_KEEPALIVE_REQUESTS" "100" }}; - - server_tokens {{ getenv "NGINX_SERVER_TOKENS" "off" }}; - - gzip {{ getenv "NGINX_GZIP" "on" }}; - - include /etc/nginx/conf.d/*.conf; -} diff --git a/config/nginx/templates/vhost.conf.tmpl b/config/nginx/templates/vhost.conf.tmpl deleted file mode 100644 index c9ae55f05..000000000 --- a/config/nginx/templates/vhost.conf.tmpl +++ /dev/null @@ -1,78 +0,0 @@ -server { - listen {{ getenv "NGINX_LISTEN_PORT" "80" }} default_server{{ if getenv "NGINX_HTTP2" }} http2{{ end }}; - server_name {{ getenv "NGINX_SERVER_NAME" "default" }}; - - root {{ getenv "NGINX_SERVER_ROOT" "/var/www/app/web" }}; - index index.php index.html; - - location = /favicon.ico { - log_not_found off; - access_log {{ getenv "NGINX_STATIC_ACCESS_LOG" "off" }}; - } - - location = /robots.txt { - allow all; - log_not_found off; - access_log {{ getenv "NGINX_STATIC_ACCESS_LOG" "off" }}; - } - - location ~ \..*/.*\.php$ { - return 403; - } - - location ~ ^/sites/.*/private/ { - return 403; - } - - # Block access to scripts in site files directory - location ~ ^/sites/[^/]+/files/.*\.php$ { - deny all; - } - - # Allow "Well-Known URIs" as per RFC 5785 - location ~* ^/.well-known/ { - allow all; - } - - # Block access to "hidden" files and directories whose names begin with a - # period. This includes directories used by version control systems such - # as Subversion or Git to store control files. - location ~ (^|/)\. { - return 403; - } - - location / { - try_files $uri /index.php?$query_string; - } - - location @rewrite { - rewrite ^/(.*)$ /index.php?q=$1; - } - - location ~ '\.php$|^/update.php' { - fastcgi_split_path_info ^(.+?\.php)(|/.*)$; - include fastcgi_params; - # Block httpoxy attacks. See https://httpoxy.org/. - fastcgi_param HTTP_PROXY ""; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param PATH_INFO $fastcgi_path_info; - fastcgi_param QUERY_STRING $query_string; - fastcgi_intercept_errors {{ getenv "NGINX_FASTCGI_INTERCEPT_ERRORS" "on" }}; - fastcgi_pass unix:/var/run/php-fpm.sock; - } - - # Fighting with Styles? This little gem is amazing. - location ~ ^/sites/.*/files/styles/ { - try_files $uri @rewrite; - } - - # Handle private files through Drupal. Private file's path can come - # with a language prefix. - location ~ ^(/[a-z\-]+)?/system/files/ { - try_files $uri /index.php?$query_string; - } - - location ~* ^(?:.+\.(?:htaccess|make|txt|engine|inc|info|install|module|profile|po|pot|sh|.*sql|test|theme|tpl(?:\.php)?|xtmpl)|code-style\.pl|/Entries.*|/Repository|/Root|/Tag|/Template)$ { - return 404; - } -} diff --git a/config/php/conf.d/docker-php-ext-apcu.ini.toml b/config/php/conf.d/docker-php-ext-apcu.ini.toml deleted file mode 100644 index 9cabe33fe..000000000 --- a/config/php/conf.d/docker-php-ext-apcu.ini.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "docker-php-ext-apcu.ini.tmpl" -dest = "/usr/local/etc/php/conf.d/docker-php-ext-apcu.ini" diff --git a/config/php/conf.d/docker-php-ext-opcache.ini.toml b/config/php/conf.d/docker-php-ext-opcache.ini.toml deleted file mode 100644 index 59c27c8ae..000000000 --- a/config/php/conf.d/docker-php-ext-opcache.ini.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "docker-php-ext-opcache.ini.tmpl" -dest = "/usr/local/etc/php/conf.d/docker-php-ext-opcache.ini" diff --git a/config/php/conf.d/docker-php-ext-xdebug.ini.toml b/config/php/conf.d/docker-php-ext-xdebug.ini.toml deleted file mode 100644 index f10c1d7f3..000000000 --- a/config/php/conf.d/docker-php-ext-xdebug.ini.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "docker-php-ext-xdebug.ini.tmpl" -dest = "/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini" diff --git a/config/php/conf.d/docker-php.ini.toml b/config/php/conf.d/docker-php.ini.toml deleted file mode 100644 index 22ab90dd7..000000000 --- a/config/php/conf.d/docker-php.ini.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "docker-php.ini.tmpl" -dest = "/usr/local/etc/php/conf.d/docker-php.ini" diff --git a/config/php/conf.d/zz-www.conf.toml b/config/php/conf.d/zz-www.conf.toml deleted file mode 100644 index 690939af5..000000000 --- a/config/php/conf.d/zz-www.conf.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "zz-www.conf.tmpl" -dest = "/usr/local/etc/php-fpm.d/zz-www.conf" diff --git a/config/php/templates/docker-php-ext-apcu.ini.tmpl b/config/php/templates/docker-php-ext-apcu.ini.tmpl deleted file mode 100644 index f406016f1..000000000 --- a/config/php/templates/docker-php-ext-apcu.ini.tmpl +++ /dev/null @@ -1,4 +0,0 @@ -[apcu] -extension=apcu.so -apc.shm_size = {{ getenv "PHP_APCU_SHM_SIZE" "128M" }} -apc.enable_cli = {{ getenv "PHP_APCU_ENABLE_CLI" "Off" }} diff --git a/config/php/templates/docker-php-ext-opcache.ini.tmpl b/config/php/templates/docker-php-ext-opcache.ini.tmpl deleted file mode 100644 index b132edbfa..000000000 --- a/config/php/templates/docker-php-ext-opcache.ini.tmpl +++ /dev/null @@ -1,9 +0,0 @@ -[opcache] -zend_extension = opcache.so -opcache.enable = {{ getenv "PHP_OPCACHE_ENABLE" "On" }} -opcache.enable_cli = {{ getenv "PHP_OPCACHE_ENABLE_CLI" "Off" }} -opcache.memory_consumption = {{ getenv "PHP_OPCACHE_MEMORY_CONSUMPTION" "128" }} -opcache.interned_strings_buffer = {{ getenv "PHP_OPCACHE_INTERNED_STRINGS_BUFFER" "8" }} -opcache.max_accelerated_files = {{ getenv "PHP_OPCACHE_MAX_ACCELERATED_FILES" "4096" }} -opcache.validate_timestamps = {{ getenv "PHP_OPCACHE_VALIDATE_TIMESTAMPS" "On" }} -opcache.revalidate_freq = {{ getenv "PHP_OPCACHE_REVALIDATE_FREQ" "2" }} diff --git a/config/php/templates/docker-php-ext-xdebug.ini.tmpl b/config/php/templates/docker-php-ext-xdebug.ini.tmpl deleted file mode 100644 index ab4692c38..000000000 --- a/config/php/templates/docker-php-ext-xdebug.ini.tmpl +++ /dev/null @@ -1,26 +0,0 @@ -{{ if getenv "PHP_XDEBUG" }} -[xdebug] -zend_extension = xdebug.so - -xdebug.coverage_enable = {{ getenv "PHP_XDEBUG_COVERAGE_ENABLE" "On" }} -xdebug.default_enable = {{ getenv "PHP_XDEBUG_DEFAULT_ENABLE" "On" }} - -xdebug.remote_enable = {{ getenv "PHP_XDEBUG_REMOTE_ENABLE" "On" }} -xdebug.remote_handler = {{ getenv "PHP_XDEBUG_REMOTE_HANDLER" "dbgp" }} -xdebug.remote_connect_back = {{ getenv "PHP_XDEBUG_REMOTE_CONNECT_BACK" "On" }} -xdebug.remote_host = {{ getenv "PHP_XDEBUG_REMOTE_HOST" "localhost" }} -xdebug.remote_port = {{ getenv "PHP_XDEBUG_REMOTE_PORT" "9000" }} -xdebug.remote_log = "{{ getenv "PHP_XDEBUG_REMOTE_LOG" "" }}" -xdebug.remote_autostart = {{ getenv "PHP_XDEBUG_REMOTE_AUTOSTART" "On" }} - -xdebug.profiler_enable = {{ getenv "PHP_XDEBUG_PROFILER_ENABLE" "Off" }} -xdebug.profiler_enable_trigger = {{ getenv "PHP_XDEBUG_PROFILER_ENABLE_TRIGGER" "On" }} -xdebug.profiler_enable_trigger_value = "{{ getenv "PHP_XDEBUG_PROFILER_ENABLE_TRIGGER_VALUE" "XDEBUG_PROFILE" }}" -xdebug.profiler_output_dir = {{ getenv "FILES_DIR" }}/xdebug/profiler -xdebug.profiler_output_name = {{ getenv "PHP_XDEBUG_PROFILER_OUTPUT_NAME" "cachegrind.out.%p" }} - -xdebug.idekey = {{ getenv "PHP_XDEBUG_IDEKEY" "PHPSTORM"}} - -xdebug.max_nesting_level = {{ getenv "PHP_XDEBUG_MAX_NESTING_LEVEL" "256" }} - -{{ end }} diff --git a/config/php/templates/docker-php.ini.tmpl b/config/php/templates/docker-php.ini.tmpl deleted file mode 100644 index 97f38521c..000000000 --- a/config/php/templates/docker-php.ini.tmpl +++ /dev/null @@ -1,422 +0,0 @@ -; This file is used to override the default php.ini values -; BASIC SETTINGS: $PHP_INI_DIR/php.ini -; PHP-FPM SETTINGS: /usr/local/etc/php-fpm.d/zz-www.conf - -[PHP] - -;;;;;;;;;;;;;;;;;;;; -; Language Options ; -;;;;;;;;;;;;;;;;;;;; - -; Output buffering is a mechanism for controlling how much output data -; (excluding headers and cookies) PHP should keep internally before pushing that -; data to the client. If your application's output exceeds this setting, PHP -; will send that data in chunks of roughly the size you specify. -; Turning on this setting and managing its maximum buffer size can yield some -; interesting side-effects depending on your application and web server. -; You may be able to send headers and cookies after you've already sent output -; through print or echo. You also may see performance benefits if your server is -; emitting less packets due to buffered output versus PHP streaming the output -; as it gets it. On production servers, 4096 bytes is a good setting for performance -; reasons. -; Note: Output buffering can also be controlled via Output Buffering Control -; functions. -; Possible Values: -; On = Enabled and buffer is unlimited. (Use with caution) -; Off = Disabled -; Integer = Enables the buffer and sets its maximum size in bytes. -; Note: This directive is hardcoded to Off for the CLI SAPI -; Default Value: Off -; Development Value: 4096 -; Production Value: 4096 -; http://php.net/output-buffering -output_buffering = {{ getenv "PHP_OUTPUT_BUFFERING" "4096" }} - -; Duration of time, in seconds for which to cache realpath information for a given -; file or directory. For systems with rarely changing files, consider increasing this -; value. -; http://php.net/realpath-cache-ttl -realpath_cache_ttl = {{ getenv "PHP_REALPATH_CACHE_TTL" "120" }} - -; URL rewriter function rewrites URL on the fly by using -; output buffer. You can set target tags by this configuration. -; "form" tag is special tag. It will add hidden input tag to pass values. -; Refer to session.trans_sid_tags for usage. -; Default Value: "form=" -; Development Value: "form=" -; Production Value: "form=" -url_rewriter.tags = "{{ getenv "PHP_URL_REWRITER_TAGS" "a=href,area=href,frame=src,input=src,form=fakeentry" }}" - -;;;;;;;;;;;;;;;;; -; Miscellaneous ; -;;;;;;;;;;;;;;;;; - -; Decides whether PHP may expose the fact that it is installed on the server -; (e.g. by adding its signature to the Web server header). It is no security -; threat in any way, but it makes it possible to determine whether you use PHP -; on your server or not. -; http://php.net/expose-php -expose_php = {{ getenv "PHP_EXPOSE_PHP" "On" }} - -;;;;;;;;;;;;;;;;;;; -; Resource Limits ; -;;;;;;;;;;;;;;;;;;; - -; Maximum execution time of each script, in seconds -; http://php.net/max-execution-time -; Note: This directive is hardcoded to 0 for the CLI SAPI -max_execution_time = {{ getenv "PHP_MAX_EXECUTION_TIME" "120" }} - -; Maximum amount of time each script may spend parsing request data. It's a good -; idea to limit this time on productions servers in order to eliminate unexpectedly -; long running scripts. -; Note: This directive is hardcoded to -1 for the CLI SAPI -; Default Value: -1 (Unlimited) -; Development Value: 60 (60 seconds) -; Production Value: 60 (60 seconds) -; http://php.net/max-input-time -max_input_time = {{ getenv "PHP_MAX_INPUT_TIME" "60" }} - -; How many GET/POST/COOKIE input variables may be accepted -max_input_vars = {{ getenv "PHP_MAX_INPUT_VARS" "1000" }} - -; Maximum amount of memory a script may consume (512MB) -; http://php.net/memory-limit -memory_limit = {{ getenv "PHP_MEMORY_LIMIT" "512M" }} - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; Error handling and logging ; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -; This directive informs PHP of which errors, warnings and notices you would like -; it to take action for. The recommended way of setting values for this -; directive is through the use of the error level constants and bitwise -; operators. The error level constants are below here for convenience as well as -; some common settings and their meanings. -; By default, PHP is set to take action on all errors, notices and warnings EXCEPT -; those related to E_NOTICE and E_STRICT, which together cover best practices and -; recommended coding standards in PHP. For performance reasons, this is the -; recommend error reporting setting. Your production server shouldn't be wasting -; resources complaining about best practices and coding standards. That's what -; development servers and development settings are for. -; Note: The php.ini-development file has this setting as E_ALL. This -; means it pretty much reports everything which is exactly what you want during -; development and early testing. -; -; Error Level Constants: -; E_ALL - All errors and warnings (includes E_STRICT as of PHP 5.4.0) -; E_ERROR - fatal run-time errors -; E_RECOVERABLE_ERROR - almost fatal run-time errors -; E_WARNING - run-time warnings (non-fatal errors) -; E_PARSE - compile-time parse errors -; E_NOTICE - run-time notices (these are warnings which often result -; from a bug in your code, but it's possible that it was -; intentional (e.g., using an uninitialized variable and -; relying on the fact it is automatically initialized to an -; empty string) -; E_STRICT - run-time notices, enable to have PHP suggest changes -; to your code which will ensure the best interoperability -; and forward compatibility of your code -; E_CORE_ERROR - fatal errors that occur during PHP's initial startup -; E_CORE_WARNING - warnings (non-fatal errors) that occur during PHP's -; initial startup -; E_COMPILE_ERROR - fatal compile-time errors -; E_COMPILE_WARNING - compile-time warnings (non-fatal errors) -; E_USER_ERROR - user-generated error message -; E_USER_WARNING - user-generated warning message -; E_USER_NOTICE - user-generated notice message -; E_DEPRECATED - warn about code that will not work in future versions -; of PHP -; E_USER_DEPRECATED - user-generated deprecation warnings -; -; Common Values: -; E_ALL (Show all errors, warnings and notices including coding standards.) -; E_ALL & ~E_NOTICE (Show all errors, except for notices) -; E_ALL & ~E_NOTICE & ~E_STRICT (Show all errors, except for notices and coding standards warnings.) -; E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR (Show only errors) -; Default Value: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED -; Development Value: E_ALL -; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT -; http://php.net/error-reporting -error_reporting = {{ getenv "PHP_ERROR_REPORTING" "E_ALL & ~E_DEPRECATED & ~E_STRICT" }} - -; This directive controls whether or not and where PHP will output errors, -; notices and warnings too. Error output is very useful during development, but -; it could be very dangerous in production environments. Depending on the code -; which is triggering the error, sensitive information could potentially leak -; out of your application such as database usernames and passwords or worse. -; For production environments, we recommend logging errors rather than -; sending them to STDOUT. -; Possible Values: -; Off = Do not display any errors -; stderr = Display errors to STDERR (affects only CGI/CLI binaries!) -; On or stdout = Display errors to STDOUT -; Default Value: On -; Development Value: On -; Production Value: Off -; http://php.net/display-errors -display_errors = {{ getenv "PHP_DISPLAY_ERRORS" "Off" }} - -; The display of errors which occur during PHP's startup sequence are handled -; separately from display_errors. PHP's default behavior is to suppress those -; errors from clients. Turning the display of startup errors on can be useful in -; debugging configuration problems. We strongly recommend you -; set this to 'off' for production servers. -; Default Value: Off -; Development Value: On -; Production Value: Off -; http://php.net/display-startup-errors -display_startup_errors = {{ getenv "PHP_DISPLAY_STARTUP_ERRORS" "Off" }} - -; Besides displaying errors, PHP can also log errors to locations such as a -; server-specific log, STDERR, or a location specified by the error_log -; directive found below. While errors should not be displayed on productions -; servers they should still be monitored and logging is a great way to do that. -; Default Value: Off -; Development Value: On -; Production Value: On -; http://php.net/log-errors -log_errors = {{ getenv "PHP_LOG_ERRORS" "On" }} - -; Set maximum length of log_errors. In error_log information about the source is -; added. The default is 1024 and 0 allows to not apply any maximum length at all. -; http://php.net/log-errors-max-len -log_errors_max_len = {{ getenv "PHP_LOG_ERRORS_MAX_LEN" "1024" }} - -;;;;;;;;;;;;;;;;; -; Data Handling ; -;;;;;;;;;;;;;;;;; - -; Maximum size of POST data that PHP will accept. -; Its value may be 0 to disable the limit. It is ignored if POST data reading -; is disabled through enable_post_data_reading. -; http://php.net/post-max-size -post_max_size = {{ getenv "PHP_POST_MAX_SIZE" "32M" }} - -; Automatically add files before PHP document. -; http://php.net/auto-prepend-file -auto_prepend_file = {{ getenv "PHP_AUTO_PREPEND_FILE" }} - -; Automatically add files after PHP document. -; http://php.net/auto-append-file -auto_append_file = {{ getenv "PHP_AUTO_APPEND_FILE" }} - -;;;;;;;;;;;;;;;;;;;;;;;;; -; Paths and Directories ; -;;;;;;;;;;;;;;;;;;;;;;;;; - -; Determines the size of the realpath cache to be used by PHP. This value should -; be increased on systems where PHP opens many files to reflect the quantity of -; the file operations performed. -; Note: if open_basedir is set, the cache is disabled -; http://php.net/realpath-cache-size -realpath_cache_size = {{ getenv "PHP_REALPATH_CACHE_SIZE" "800K" }} - -;;;;;;;;;;;;;;;; -; File Uploads ; -;;;;;;;;;;;;;;;; - -; Maximum allowed size for uploaded files. -; http://php.net/upload-max-filesize -upload_max_filesize = {{ getenv "PHP_UPLOAD_MAX_FILESIZE" "100M" }} - -; Maximum number of files that can be uploaded via a single request -max_file_uploads = {{ getenv "PHP_MAX_FILE_UPLOADS" "20" }} - -;;;;;;;;;;;;;;;;;; -; Fopen wrappers ; -;;;;;;;;;;;;;;;;;; - -; Whether to allow the treatment of URLs (like http:// or ftp://) as files. -; http://php.net/allow-url-fopen -allow_url_fopen = {{ getenv "PHP_ALLOW_URL_FOPEN" "On" }} - -; Default timeout for socket based streams (seconds) -; http://php.net/default-socket-timeout -default_socket_timeout = {{ getenv "PHP_DEFAULT_SOCKET_TIMEOUT" "60" }} - -;;;;;;;;;;;;;;;;;;; -; Module Settings ; -;;;;;;;;;;;;;;;;;;; - -[Date] - -; Defines the default timezone used by the date functions -; http://php.net/date.timezone -date.timezone = {{ getenv "PHP_DATE_TIMEZONE" "America/Toronto"}} - -[Pdo_mysql] - -; If mysqlnd is used: Number of cache slots for the internal result set cache -; http://php.net/pdo_mysql.cache_size -pdo_mysql.cache_size = {{ getenv "PHP_PDO_MYSQL_CACHE_SIZE" "2000" }} - -[mail function] - -; For Unix only. You may supply arguments as well (default: "sendmail -t -i") -; or /usr/sbin/sendmail -t -i -; http://php.net/sendmail-path -sendmail_path = {{ getenv "PHP_SENDMAIL_PATH" "/bin/true" }} - -; Add X-PHP-Originating-Script: that will include uid of the script followed by the filename -mail.add_x_header = {{ getenv "PHP_MAIL_ADD_X_HEADER" "On" }} - -[ODBC] - -; Handling of binary data. 0 means passthru, 1 return as is, 2 convert to char. -; See the documentation on odbc_binmode and odbc_longreadlen for an explanation -; of odbc.defaultlrl and odbc.defaultbinmode -; http://php.net/odbc.defaultbinmode -odbc.defaultbinmode = 1 - -[MySQLi] - -; If mysqlnd is used: Number of cache slots for the internal result set cache -; http://php.net/mysqli.cache_size -mysqli.cache_size = {{ getenv "PHP_MYSQLI_CACHE_SIZE" "2000" }} - -[Session] - -; Handler used to store/retrieve data. -; http://php.net/session.save-handler -session.save_handler = {{ getenv "PHP_SESSION_SAVE_HANDLER" "files" }} -; Argument passed to save_handler. In the case of files, this is the path -; where data files are stored. Note: Windows users have to change this -; variable in order to use PHP's session functions. -; -; The path can be defined as: -; -; session.save_path = "N;/path" -; -; where N is an integer. Instead of storing all the session files in -; /path, what this will do is use subdirectories N-levels deep, and -; store the session data in those directories. This is useful if -; your OS has problems with many files in one directory, and is -; a more efficient layout for servers that handle many sessions. -; -; NOTE 1: PHP will not create this directory structure automatically. -; You can use the script in the ext/session dir for that purpose. -; NOTE 2: See the section on garbage collection below if you choose to -; use subdirectories for session storage -; -; The file storage module creates files using mode 600 by default. -; You can change that by using -; -; session.save_path = "N;MODE;/path" -; -; where MODE is the octal representation of the mode. Note that this -; does not overwrite the process's umask. -; http://php.net/session.save-path -session.save_path = "{{ getenv "PHP_SESSION_SAVE_PATH" "/tmp" }}" -; Whether to use cookies. -; http://php.net/session.use-cookies -session.use_cookies = {{ getenv "PHP_SESSION_USE_COOKIES" "1" }} -; This option forces PHP to fetch and use a cookie for storing and maintaining -; the session id. We encourage this operation as it's very helpful in combating -; session hijacking when not specifying and managing your own session id. It is -; not the be-all and end-all of session hijacking defense, but it's a good start. -; http://php.net/session.use-only-cookies -session.use_only_cookies = {{ getenv "PHP_SESSION_USE_ONLY_COOKIES" "1" }} -; Name of the session (used as cookie name). -; http://php.net/session.name -session.name = {{ getenv "PHP_SESSION_NAME" "PHPSESSID" }} -; Initialize session on request startup. -; http://php.net/session.auto-start -session.auto_start = {{ getenv "PHP_SESSION_AUTO_START" "0" }} - -; Lifetime in seconds of cookie or, if 0, until browser is restarted. -; http://php.net/session.cookie-lifetime -session.cookie_lifetime = {{ getenv "PHP_SESSION_COOKIE_LIFETIME" "2000" }} -; The path for which the cookie is valid. -; http://php.net/session.cookie-path -session.cookie_path = {{ getenv "PHP_SESSION_COOKIE_PATH" "/" }} -; The domain for which the cookie is valid. -; http://php.net/session.cookie-domain -session.cookie_domain = {{ getenv "PHP_SESSION_COOKIE_DOMAIN" }} -; Whether or not to add the httpOnly flag to the cookie, which makes it inaccessible to browser scripting languages such as JavaScript. -; http://php.net/session.cookie-httponly -session.cookie_httponly = {{ getenv "PHP_SESSION_COOKIE_HTTPONLY" }} - -; Handler used to serialize data. php is the standard serializer of PHP. -; http://php.net/session.serialize-handler -session.serialize_handler = {{ getenv "PHP_SESSION_SERIALIZE_HANDLER" "php" }} - -; Defines the probability that the 'garbage collection' process is started -; on every session initialization. The probability is calculated by using -; gc_probability/gc_divisor. Where session.gc_probability is the numerator -; and gc_divisor is the denominator in the equation. Setting this value to 1 -; when the session.gc_divisor value is 100 will give you approximately a 1% chance -; the gc will run on any give request. -; Default Value: 1 -; Development Value: 1 -; Production Value: 1 -; http://php.net/session.gc-probability -session.gc_probability = {{ getenv "PHP_SESSION_GC_PROBABILITY" "1" }} -; Defines the probability that the 'garbage collection' process is started on every -; session initialization. The probability is calculated by using the following equation: -; gc_probability/gc_divisor. Where session.gc_probability is the numerator and -; session.gc_divisor is the denominator in the equation. Setting this value to 1 -; when the session.gc_divisor value is 100 will give you approximately a 1% chance -; the gc will run on any give request. Increasing this value to 1000 will give you -; a 0.1% chance the gc will run on any give request. For high volume production servers, -; this is a more efficient approach. -; Default Value: 100 -; Development Value: 1000 -; Production Value: 1000 -; http://php.net/session.gc-divisor -session.gc_divisor = {{ getenv "PHP_SESSION_GC_DIVISOR" "100" }} -; After this number of seconds, stored data will be seen as 'garbage' and -; cleaned up by the garbage collection process. -; http://php.net/session.gc-maxlifetime -session.gc_maxlifetime = {{ getenv "PHP_SESSION_GC_MAXLIFETIME" "1440" }} - -; Check HTTP Referer to invalidate externally stored URLs containing ids. -; HTTP_REFERER has to contain this substring for the session to be -; considered as valid. -; http://php.net/session.referer-check -session.referer_check = {{ getenv "PHP_SESSION_REFERER_CHECK" "" }} - -; Set to {nocache,private,public,} to determine HTTP caching aspects -; or leave this empty to avoid sending anti-caching headers. -; http://php.net/session.cache-limiter -session.cache_limiter = {{ getenv "PHP_SESSION_CACHE_LIMITER" "nocache" }} -; Document expires after n minutes. -; http://php.net/session.cache-expire -session.cache_expire = {{ getenv "PHP_SESSION_CACHE_EXPIRE" "180" }} - -; trans sid support is disabled by default. -; Use of trans sid may risk your users' security. -; Use this option with caution. -; - User may send URL contains active session ID -; to other person via. email/irc/etc. -; - URL that contains active session ID may be stored -; in publicly accessible computer. -; - User may access your site with the same session ID -; always using URL stored in browser's history or bookmarks. -; http://php.net/session.use-trans-sid -session.use_trans_sid = {{ getenv "PHP_SESSION_USE_TRANS_SID" "0" }} - -; Set session ID character length. This value could be between 22 to 256. -; Shorter length than default is supported only for compatibility reason. -; Users should use 32 or more chars. -; http://php.net/session.sid-length -; Default Value: 32 -; Development Value: 26 -; Production Value: 26 -session.sid_length = {{ getenv "PHP_SESSION_SID_LENGTH" "32" }} - -; Define how many bits are stored in each character when converting -; the binary hash data to something readable. -; Possible values: -; 4 (4 bits: 0-9, a-f) -; 5 (5 bits: 0-9, a-v) -; 6 (6 bits: 0-9, a-z, A-Z, "-", ",") -; Default Value: 4 -; Development Value: 5 -; Production Value: 5 -; http://php.net/session.hash-bits-per-character -session.sid_bits_per_character = {{ getenv "PHP_SESSION_SID_BITS_PER_CHARACTER" "5" }} - -[Assertion] -zend.assertions = {{ getenv "PHP_ZEND_ASSERTIONS" "1" }} -assert.active = {{ getenv "PHP_ASSERT_ACTIVE" "On" }} diff --git a/config/php/templates/zz-www.conf.tmpl b/config/php/templates/zz-www.conf.tmpl deleted file mode 100644 index 1432222b3..000000000 --- a/config/php/templates/zz-www.conf.tmpl +++ /dev/null @@ -1,70 +0,0 @@ -[www] - -; Unix user/group of processes -; Note: The user is mandatory. If the group is not set, the default user's group -; will be used. -user = {{ getenv "PHP_FPM_USER" "www-data" }} -group = {{ getenv "PHP_FPM_GROUP" "www-data" }} - -listen = /var/run/php-fpm.sock -listen.owner = {{ getenv "PHP_FPM_USER" "www-data" }} -listen.group = {{ getenv "PHP_FPM_GROUP" "www-data" }} - -; Redirect worker stdout and stderr into main error log. If not set, stdout and -; stderr will be redirected to /dev/null according to FastCGI specs. -; Note: on highloaded environement, this can cause some delay in the page -; process time (several ms). -; Default Value: no -clear_env = {{ getenv "PHP_FPM_CLEAR_ENV" "yes" }} - -; Choose how the process manager will control the number of child processes. -; Possible Values: -; static - a fixed number (pm.max_children) of child processes; -; dynamic - the number of child processes are set dynamically based on the -; following directives. With this process management, there will be -; always at least 1 children. -; pm.max_children - the maximum number of children that can -; be alive at the same time. -; pm.start_servers - the number of children created on startup. -; pm.min_spare_servers - the minimum number of children in 'idle' -; state (waiting to process). If the number -; of 'idle' processes is less than this -; number then some children will be created. -; pm.max_spare_servers - the maximum number of children in 'idle' -; state (waiting to process). If the number -; of 'idle' processes is greater than this -; number then some children will be killed. -; ondemand - no children are created at startup. Children will be forked when -; new requests will connect. The following parameter are used: -; pm.max_children - the maximum number of children that -; can be alive at the same time. -; pm.process_idle_timeout - The number of seconds after which -; an idle process will be killed. -; Note: This value is mandatory. -pm = {{ getenv "PHP_FPM_PM" "dynamic" }} -; The number of child processes to be created when pm is set to 'static' and the -; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. -; This value sets the limit on the number of simultaneous requests that will be -; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. -; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP -; CGI. The below defaults are based on a server without much resources. Don't -; forget to tweak pm.* to fit your needs. -; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' -; Note: This value is mandatory. Default docker is 5. -pm.max_children = {{ getenv "PHP_FPM_PM_MAX_CHILDREN" "20" }} -; The number of child processes created on startup. -; Note: Used only when pm is set to 'dynamic' -; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 -pm.start_servers = {{ getenv "PHP_FPM_PM_START_SERVERS" "5" }} -; The desired minimum number of idle server processes. -; Note: Used and Mandatory only when pm is set to 'dynamic' -pm.min_spare_servers = {{ getenv "PHP_FPM_PM_MIN_SPARE_SERVERS" "5" }} -; The desired maximum number of idle server processes. -; Note: Used only when pm is set to 'dynamic' -; Note: Mandatory when pm is set to 'dynamic' -pm.max_spare_servers = {{ getenv "PHP_FPM_PM_MAX_SPARE_SERVERS" "5" }} -; The number of requests each child process should execute before respawning. -; This can be useful to work around memory leaks in 3rd party libraries. For -; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. -; Default Value: 0 -pm.max_requests = {{ getenv "PHP_FPM_PM_MAX_REQUESTS" "500" }} From f25698813580dc2a64cd907c60f8d4b2a201106f Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 15:32:44 -0400 Subject: [PATCH 014/107] Updated init.sh to include needed packages and removed app bin folder since now it will be copied from the base image. --- scripts/app_bin/config_app_runner_user | 33 ---- scripts/app_bin/docker-webserver-entrypoint | 53 ------- scripts/app_bin/files_link | 43 ------ scripts/app_bin/install_drupal | 63 -------- scripts/app_bin/wait_for_database | 37 ----- scripts/drupal/init.sh | 161 +++++++++++++++----- 6 files changed, 123 insertions(+), 267 deletions(-) delete mode 100644 scripts/app_bin/config_app_runner_user delete mode 100644 scripts/app_bin/docker-webserver-entrypoint delete mode 100644 scripts/app_bin/files_link delete mode 100644 scripts/app_bin/install_drupal delete mode 100644 scripts/app_bin/wait_for_database diff --git a/scripts/app_bin/config_app_runner_user b/scripts/app_bin/config_app_runner_user deleted file mode 100644 index 02c5b445f..000000000 --- a/scripts/app_bin/config_app_runner_user +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [[ -n "${DEBUG}" ]]; then - set -x -fi - -# Adjust permission so that the regular user own the default home dir -chown ${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /home -# Make sure that app runner user can add supervisord and php-fpm sockets into /var/run folder -chown ${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /var/run && chmod g+w /var/run - -if [[ "${DEFAULT_USER}" == "${APP_RUNNER_USER}" ]]; then - echo "[info] The \"user\" directive makes sense only if the master process runs with super-user privileges" - sed -i -e '/user/!b' -e '/'${NGINX_USER}'/!b' -e '/'${NGINX_USER}'/d' /etc/nginx/nginx.conf - - # Make sure that every php-fpm conf file in /usr/local/etc/php-fpm.d/ doesn't have user or group directive enabled - for php_fpm_conf in /usr/local/etc/php-fpm.d/*.conf; do - sed -i 's/^user = /;&/' ${php_fpm_conf} - sed -i 's/^group = /;&/' ${php_fpm_conf} - done -fi - -# Allow app runner user to write into nginx logs -chown -R root:${APP_RUNNER_GROUP} /var/log/nginx -chmod -R g+w /var/log/nginx - -# Override native user and use the "_www" one created in the image -uid=$(stat -c %u /srv) -gid=$(stat -c %g /srv) -sed -i -r "s/${APP_RUNNER_USER}:x:\d+:\d+:/${APP_RUNNER_GROUP}:x:$uid:$gid:/g" /etc/passwd -sed -i -r "s/${APP_RUNNER_GROUP}:x:\d+:/${APP_RUNNER_GROUP}:x:$gid:/g" /etc/group diff --git a/scripts/app_bin/docker-webserver-entrypoint b/scripts/app_bin/docker-webserver-entrypoint deleted file mode 100644 index 4dfa73189..000000000 --- a/scripts/app_bin/docker-webserver-entrypoint +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [[ -n "${DEBUG}" ]]; then - set -x -fi - -# usage: file_env VAR [DEFAULT] -# ie: file_env 'DB_PASSWORD' 'example' -# (will allow for "$DB_PASSWORD_FILE" to fill in the value of -# "$DB_PASSWORD" from a file, especially for Docker's secrets feature) -file_env() { - local var="$1" - local fileVar="${var}_FILE" - local def="${2:-}" - if [[ "${!var:-}" ]] && [[ "${!fileVar:-}" ]]; then - echo >&2 "error: both $var and $fileVar are set (but are exclusive)" - exit 1 - fi - local val="${def}" - if [[ "${!var:-}" ]]; then - val="${!var}" - elif [[ "${!fileVar:-}" ]]; then - val="$(< "${!fileVar}")" - fi - export "$var"="$val" - unset "$fileVar" -} - -if [[ -f "${APP_DOCROOT}/sites/default/settings.isle.php" ]]; then - file_env "APP_NAME" - file_env "APP_ACCOUNT_PASS" - file_env "DB_NAME" - file_env "DB_USER" - file_env "DB_PASSWORD" -fi - -# Make sure files folder is linked at the proper folder -/usr/local/bin/files_link "${APP_DOCROOT}/sites/default/files" - -# set custom config files for the drupal .env where the credentials are. -/usr/local/bin/confd -onetime -backend env - -# Delay container to start until the database server is ready. -/usr/local/bin/wait_for_database - -# Attempt to install drupal. -if [[ "${AUTO_INSTALL}" == "true" ]]; then - /usr/local/bin/install_drupal -fi - -exec "$@" diff --git a/scripts/app_bin/files_link b/scripts/app_bin/files_link deleted file mode 100644 index bbd10f417..000000000 --- a/scripts/app_bin/files_link +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [[ -n "${DEBUG}" ]]; then - set -x -fi - -app_public_dir=$1 - -# Add symlink from persistent files volume to application's storage public dir. -if [[ -n "${app_public_dir}" ]]; then - echo >&2 - echo >&2 "[INFO] Application's public storage directory specified, trying to symlink from files persistent volume" - echo >&2 - - if [[ -d "${app_public_dir}" ]]; then - if [[ ! -L "${app_public_dir}" ]]; then - if [[ "$(ls -A "${app_public_dir}")" ]]; then - echo >&2 - echo >&2 "[ERROR] Failed to symlink public storage directory to a persistent volume" - echo >&2 " Directory ${app_public_dir} must not exists or be empty" - echo >&2 " (use files import to migrate existing public files)" - echo >&2 - exit 1 - # If dir is not symlink and empty, remove it and link. - else - echo >&2 - echo >&2 "[INFO] Empty public storage dir detected: removing and symlinking '${app_public_dir}' -> ''${FILES_DIR}/public'" - echo >&2 - rm -rf "${app_public_dir}" - ln -sf "${FILES_DIR}/public" "${app_public_dir}" - fi - else - echo "Symlink already in place" - fi - else - echo >&2 - echo >&2 "[INFO] No public storage dir detected: just symlinking '${app_public_dir}' -> '${FILES_DIR}/public'" - echo >&2 - ln -sf "${FILES_DIR}/public" "${app_public_dir}" - fi -fi diff --git a/scripts/app_bin/install_drupal b/scripts/app_bin/install_drupal deleted file mode 100644 index 7de9198cf..000000000 --- a/scripts/app_bin/install_drupal +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [[ -n "${DEBUG}" ]]; then - set -x -fi - -if [[ "${APP_ENV}" == "dev" && ! -f "${APP_DOCROOT}/index.php" ]]; then - cd ${APP_ROOT} - composer install --classmap-authoritative -fi; - -# Ensure we are in the app web. -cd ${APP_DOCROOT} - -# Determine if site already installed -site_installed=$(${APP_ROOT}/vendor/bin/drush status bootstrap | grep -q Successful; echo "$?") - -if [[ -n "${site_installed}" && "${site_installed}" -eq 0 ]]; then - echo >&2 - echo >&2 '[INFO] Site already installed' - echo >&2 ' You might need to run ../vendor/bin/drush config-import if' - echo >&2 ' the database configurations are out of sync.' - echo >&2 -else - site_install_options=( - --yes - --verbose - --account-name=${APP_ACCOUNT_NAME} - --account-mail=${APP_ACCOUNT_MAIL} - --account-pass=${APP_ACCOUNT_PASS} - ) - if [[ "$(ls -A ${APP_ROOT}/config/sync)" && -f ${APP_ROOT}/config/sync/core.extension.yml ]]; then - echo >&2 - echo >&2 '[INFO] Installing site using existing configurations' - echo >&2 '[INFO] Switching to minimal profile to allow existing config to install.' - echo >&2 ' See https://www.drupal.org/node/2897299 for more information.' - echo >&2 - sed -i 's/standard:/minimal:/g' ${APP_ROOT}/config/sync/core.extension.yml - sed -i 's/profile: standard/profile: minimal/g' ${APP_ROOT}/config/sync/core.extension.yml - site_install_options+=( - --existing-config - minimal - ) - else - echo >&2 - echo >&2 '[INFO] Installing site from scratch' - echo >&2 - site_install_options+=( - --site-name=${APP_NAME} - --site-mail=${APP_ACCOUNT_MAIL} - standard - ) - fi - - if [[ -n "${DEBUG}" ]]; then - site_install_options+=( --debug ) - fi - - # Installing drupal - ${APP_ROOT}/vendor/bin/drush site:install "${site_install_options[@]}" -fi diff --git a/scripts/app_bin/wait_for_database b/scripts/app_bin/wait_for_database deleted file mode 100644 index 7f2da2cc2..000000000 --- a/scripts/app_bin/wait_for_database +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [[ -n "${DEBUG}" ]]; then - set -x -fi - -function fail { - echo $1 >&2 - exit 1 -} - -function retry { - local n=1 - local max=5 - local delay=5 - while true; do - "$@" && break || { - if [[ $n -lt $max ]]; then - ((n++)) - echo "Command failed. Attempt $n/$max:" - sleep $delay; - else - fail "The command has failed after $n attempts." - fi - } - done -} - -# Check connection to the database and verify if the database is created -retry mysql --silent --quick --force \ - --host=${DB_HOST} \ - --port=${DB_PORT} \ - --user=${DB_USER} \ - --password=${DB_PASSWORD} \ - --execute "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='${DB_NAME}'" >/dev/null 2>&1 diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index b0b79d28e..96a818911 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -8,6 +8,11 @@ set -e codebase="drupal" config_dir="$PWD/config/drupal" scripts_dir="$PWD/scripts/drupal" +composer_install_run="true" +current_folder="$PWD" +composer_general_flags="--ignore-platform-reqs --no-interaction" +OS=`uname -s` +[[ "$OS" == "Darwin" ]] && is_darwin=true || is_darwin=false function fail { echo -e "\033[31m[ERROR]\033[0m $1" >&2 @@ -23,62 +28,110 @@ function help() { exit 0 } -function install_drupal() { - local composer=$(command -v composer) - local args="create-project drupal/recommended-project:^8.8 codebase" +function download_drupal() { + local args="create-project drupal/recommended-project:^8.8" local flags="--ignore-platform-reqs --no-interaction" local codebase="$1" local drush_require="require drush/drush $flags" + if [[ ! $composer ]]; then + fail "We could not download drupal. Ensure composer or docker is setup and installed properly on your local host." + fi + if [[ "$codebase" == "islandora" ]]; then - local args="create-project islandora/drupal-project codebase" + local args="create-project islandora/drupal-project" fi echo -e "\033[1m[INFO]\033[0m Installing drupal using composer" echo " " - if [[ composer ]]; then - echo " You have composer installed locally and we are using it to download the drupal project." - echo >&2 - $composer $args $flags - echo " Adding drush to the drupal repository." - echo >&2 - cd codebase && $composer $drush_require && cd .. - elif [[ ! composer ]]; then - echo " Using the official composer docker image to download drupal." - mkdir -p $HOME/.composer && docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3 $composer_cmd_arguments $composer_cmd_flags - echo " Adding drush to the drupal repository." - echo >&2 - cd codebase && docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3 $drush_require && cd .. - else - fail "We could not download drupal. Please check your arguments and try again." + + echo " Downloading the drupal codebase." + echo >&2 + cd "$current_folder/" + $composer $args $composer_general_flags codebase + + download_required_packages +} + +function download_required_packages() { + # Using two arrays cause the default MacOS bash is an older version. Probably less than 4. + local packages=("zaporylie/composer-drupal-optimizations" "vlucas/phpdotenv" "drush/drush" "cweagans/composer-patches") + local versions=("^1.1 --dev" "^4.0" "^10.0" "^1.6.7") + + echo -e "\033[1m[INFO]\033[0m Adding necessary packages to the drupal repository." + echo " " + echo >&2 + + cd "$current_folder/codebase" + + # For vlucas/phpdotenv we need the load.environment.php script to be available. + if [[ ! -f ./load.environment.php ]]; then + cp "$current_folder/config/drupal/load.environment.php" . + # Update the composer.json to include it. + local pattern=" \"extra\"" + if $is_darwin; then + local snippet=$'\ "autoload": {"files": ["load.environment.php"]},\n' + sed -i '' -e '\|^'"$pattern"'|i\'$'\n'"$snippet" composer.json + else + local snippet="\ \"autoload\": {\n \"files\": [\"load\.environment\.php\"]\n },\n" + sed -i -e "/^${pattern}/i ${snippet}" composer.json + fi fi + + for i in "${!packages[@]}"; do + local package=${packages[$i]} + local version=${versions[$i]} + # Only installing a package when it is not available in composer.json + if [[ ! $(grep "${package}" composer.json) ]]; then + $composer require ${package}:${version} $composer_general_flags + fi + done + + # Flag that we shouldn't run composer install to initialize everything. + composer_install_run="false" + cd "$current_folder" } -function setup_settings_isle_php() { - local default_dir="$PWD/codebase/web/sites/default" +function create_required_files() { + local drupal_root="$current_folder/codebase/web" + local default_dir="$drupal_root/sites/default" local settings="${default_dir}/settings.php" local settings_default="${default_dir}/default.settings.php" local settings_isle="settings.isle.php" local insert_after="\$settings\[\'entity_update_backup\'\] \= TRUE\;" + local config_sync_pattern="\$settings\['config_sync_directory'] = '\/directory\/outside\/webroot'" local snippet="${config_dir}/snippet.txt" - if [[ -f "${default_dir}/${settings_isle}" ]]; then - fail "An existing settings.isle.php was found under ${default_dir}." + # Prepare the settings file for installation. In case the user has an existing + # one we skip overriding it. + if [[ ! -f "${settings}" && -f "${settings_default}" ]]; then + cp ${settings_default} ${settings} + fi + + # Ensuring that settings.php is pointing to the proper config_sync_directory. + if [[ $(grep "^#.* ${config_sync_pattern}" ${settings} || true) ]]; then + local replace="\$settings\['config_sync_directory'] = '..\/config\/sync'" + if $is_darwin; then + sed -i '' -e "s/^#.* ${config_sync_pattern}/${replace}/" ${settings} + else + sed -i -e "s/^#.* ${config_sync_pattern}/${replace}/" ${settings} + fi fi echo -e "\033[1m[INFO]\033[0m Setting up settings.isle.php to be included in settings.php" echo " " - if [[ ! -f "${settings}" ]]; then - echo " An existing settings.php file couldn't be located and one is created from default.settings.php." - cp ${settings_default} ${settings} - else - echo " An existing settings.php file was found." - fi - sed -i '' -e "/${insert_after}/r ${snippet}" ${settings} - cp "${config_dir}/${settings_isle}" "${default_dir}/${settings_isle}" - echo " settings.isle.php was successfully setup ." - echo >&2 + # Insert settings.isle.php snippet into the settings.php file + if [[ ! -f "${default_dir}/${settings_isle}" ]]; then + if $is_darwin; then + sed -i '' -e "/${insert_after}/r ${snippet}" ${settings} + else + sed -i '' -e "/${insert_after}/r ${snippet}" ${settings} + fi + cp "${config_dir}/${settings_isle}" "${default_dir}/${settings_isle}" + echo " settings.isle.php was successfully setup." + echo >&2 + fi } while [ ! $# -eq 0 ] @@ -102,13 +155,45 @@ do shift done -# Checking if the codebase directory exists and quit. -if [[ -d "$PWD/codebase" && "$(ls -A $PWD/codebase)" ]]; then - fail "The codebase directory exists and is not empty. Please delete it and run this command again." +### +# Determine how we will be running composer. +### +composer=$(command -v composer) +if [[ ! $composer ]]; then + # We use the docker composer image to run composer related commands. + echo >&2 + echo -e "\033[1m[INFO]\033[0m Using the official composer docker image to run composer commands" + echo " " + echo >&2 + composer="mkdir -p $HOME/.composer && docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" +fi + +### +# Checking if the project code exists. +### +if [[ ! -f "$PWD/codebase/composer.json" ]]; then + download_drupal $codebase +else + download_required_packages fi +### # Initialize drupal database and files persistent storage folders. +### mkdir -p $PWD/data/drupal/files/public $PWD/data/drupal/files/private $PWD/data/drupal/database -install_drupal $codebase -setup_settings_isle_php +### +# Running composer install just in case the user has an existing project. +### +if [[ "$composer_install_run" == "true" ]]; then + cd "$current_folder/codebase" + $composer install $composer_flags + cd .. +fi + +### +# Create required files if this init was called for an existing project/codebase. +### +if [[ ! -f "$current_folder/codebase/web/sites/default/settings.isle.php" ]]; then + create_required_files +fi From dd0b376030a8cd48ef1f2fc78e5b8d2fcaa718f6 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 15:35:18 -0400 Subject: [PATCH 015/107] Added default drupal hash salt variable and pinned to an exact version for the base image. --- docker-compose.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 6db6982ec..ffe2aa065 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2.4' +version: "2.4" ## ISLE 8 Draft Prototype ## Feb, 2020 @@ -27,7 +27,7 @@ services: - code_dir=./codebase - templates_dir=./config - app_bin_dir=./scripts/app_bin - - base_image_tag=latest + - base_image_tag=7.2.28-1.17.8-0ceedc1b - build_environment=dev # XDEBUG confd build time args - you can set it to 0 turn it off. - PHP_XDEBUG=${PHP_XDEBUG:-1} @@ -59,6 +59,7 @@ services: DB_PORT: ${DB_PORT:-3306} DB_DRIVER: ${DB_DRIVER:-mysql} DRUSH_OPTIONS_URI: ${DRUPAL_LOCAL_URL:-islandora.localhost} + DRUPAL_HASH_SALT: ${DRUPAL_HASH_SALT:-tfvQNpDFG2CjY9WHGNgFqC3eoMjyg5pZdGMQ74zjmnIoe0bi8F3hUvBWnGFIAM3nnj2iWA} NGINX_LISTEN_PORT: "8080" AUTO_INSTALL: "true" volumes: @@ -96,7 +97,7 @@ services: image: traefik:2.1.3 command: --api.insecure=true --providers.docker ports: - - '8000:80' + - "8000:80" #- "443:443" #- "8080:8080" volumes: @@ -106,7 +107,6 @@ services: # - ./config/traefik/acme.json:/acme.json # SSL Choice 2: To use commercial SSLs - uncomment ONLY the line below. Add your SSL certs (.cert, .pem, .key) files to config/traefik/ssl-certs # - ./config/traefik/ssl-certs:/certs:ro - # Use Environment variables to pass in Traefik config; no traefik.yml required # by providers # Alternative to a static configureation /etc/traefik/traefik.yml" From ba72e11b5c5a0997fef35d0c3cf066a366b0cd75 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 15:36:09 -0400 Subject: [PATCH 016/107] Made sure that drupal.Dockerfile copy the templates from the base image. --- drupal.Dockerfile | 77 ++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/drupal.Dockerfile b/drupal.Dockerfile index 980dd28d6..b998134bd 100644 --- a/drupal.Dockerfile +++ b/drupal.Dockerfile @@ -1,6 +1,6 @@ ARG build_environment=prod ARG code_dir=./codebase -ARG base_image_tag=latest +ARG base_image_tag=7.2.28-1.17.8-0ceedc1b ARG composer_version=1.9.3 ARG templates_dir=./config @@ -40,20 +40,19 @@ COPY ${code_dir}/composer.json ${code_dir}/composer.lock ./ RUN set -eux; \ flags="${COMPOSER_INSTALL_FLAGS}"; \ if [ "$build_environment" == "prod" ]; then \ - flags="${COMPOSER_INSTALL_FLAGS} --no-dev"; \ + flags="${COMPOSER_INSTALL_FLAGS} --no-dev"; \ fi; \ composer install $flags \ # make dummy directory just in case no drupal contrib related dependencies was created. && for dir in $DRUPAL_COMPOSER_DIRECTORIES; do \ - if [ ! -d $dir ]; then \ - mkdir -p $dir; \ - fi; \ + if [ ! -d $dir ]; then \ + mkdir -p $dir; \ + fi; \ done; # # Stage 2: Any node related dependencies can be build here. e.g. compile scss to css # -# Example coming soon # # Stage 3: The base app/drupal @@ -82,42 +81,44 @@ ENV PATH=${PATH}:${APP_ROOT}/vendor/bin \ APP_RUNNER_GROUP=${app_runner_group} \ APP_RUNNER_GROUP_ID=${app_runner_group_id:-1000} -# Copy custom excutable scripts -COPY ${app_bin_dir} /usr/local/bin/ - -# Copy custom configuration files for PHP and NGINX -COPY ${templates_dir}/php/conf.d /etc/confd/conf.d -COPY ${templates_dir}/nginx/conf.d /etc/confd/conf.d -COPY ${templates_dir}/php/templates /etc/confd/templates -COPY ${templates_dir}/nginx/templates /etc/confd/templates +RUN ls -all /confd_templates/; \ + ls -all /drupal/; \ + ls -all /drupal/confd/ -# Make sure docker-webserver-entrypoint and other scripts are executable -RUN chmod -R +x /usr/local/bin/; \ +# Copy custom configuration template files for PHP and NGINX +RUN mkdir -p /etc/confd && cp -R /confd_templates/* /etc/confd/; \ + # Copy custom excutable scripts for drupal including the default entrypoint. + mv /drupal/bin/* /usr/local/bin/; \ + # Make sure docker-webserver-entrypoint and other scripts are executable + chmod -R +x /usr/local/bin/; \ # apply custom configurations based on confd templates /usr/local/bin/confd -onetime -backend env \ # clean the content of confd so that the app can add it's templates later in the process - && rm -rf /etc/confd/* + && rm -rf /etc/confd/* \ + # Move the .env template file for the drupal app. We then run confd in the docker + # entrypoint to place it under /.env. + && cp -R /drupal/confd/* /etc/confd/ && rm -rf /drupal/confd # Add and configure app runner user RUN set -xe; \ # Delete existing user/group if uid/gid occupied. existing_group=$(getent group "${APP_RUNNER_GROUP_ID}" | cut -d: -f1); \ if [ -n "${existing_group}" ]; then delgroup "${existing_group}"; fi; \ - existing_user=$(getent passwd "${APP_RUNNER_USER_ID}" | cut -d: -f1); \ + existing_user=$(getent passwd "${APP_RUNNER_USER_ID}" | cut -d: -f1); \ if [ -n "${existing_user}" ]; then deluser "${existing_user}"; fi; \ \ # Ensure app runner user/group exists - addgroup --system --gid ${APP_RUNNER_GROUP_ID} ${APP_RUNNER_GROUP}; \ - adduser --system --disabled-password --ingroup ${APP_RUNNER_GROUP} --shell /bin/bash --uid ${APP_RUNNER_USER_ID} ${APP_RUNNER_USER}; \ - usermod --append --groups ${NGINX_USER_GROUP} ${APP_RUNNER_USER} \ + addgroup --system --gid ${APP_RUNNER_GROUP_ID} ${APP_RUNNER_GROUP}; \ + adduser --system --disabled-password --ingroup ${APP_RUNNER_GROUP} --shell /bin/bash --uid ${APP_RUNNER_USER_ID} ${APP_RUNNER_USER}; \ + usermod --append --groups ${NGINX_USER_GROUP} ${APP_RUNNER_USER} \ # Other app runner user related configurations. See bin/config_app_runner_user && config_app_runner_user \ \ # Make sure that files dir have proper permissions. && mkdir -p ${FILES_DIR}/public; \ - mkdir -p ${FILES_DIR}/private; \ - # Ensure the files dir is owned by nginx user - chown -R ${NGINX_USER}:${NGINX_USER_GROUP} ${FILES_DIR} + mkdir -p ${FILES_DIR}/private; \ + # Ensure the files dir is owned by nginx user + chown -R ${NGINX_USER}:${NGINX_USER_GROUP} ${FILES_DIR} EXPOSE $NGINX_LISTEN_PORT ENTRYPOINT [ "docker-webserver-entrypoint" ] @@ -137,10 +138,7 @@ COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/w COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/web/profiles/contrib ./web/profiles/contrib COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/web/libraries ./web/libraries COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/drush/contrib ./drush/contrib -# If stage 2 available genrated front end css and css artifacts files can be copied - -# Copy custom configuration files for the app/drupal. We run confd in the docker entrypoint for this. -COPY ${templates_dir}/drupal/confd /etc/confd +# If stage 2 available and generated js and css artifacts files, they can also be copied inside this folder. # # Stage 4: The production setup @@ -149,7 +147,9 @@ FROM base AS prod ENV APP_ENV=prod # Using the production php.ini -RUN mv ${PHP_INI_DIR}/php.ini-production ${PHP_INI_DIR}/php.ini +RUN mv ${PHP_INI_DIR}/php.ini-production ${PHP_INI_DIR}/php.ini; \ + # Remove the confd templates altogether. + rm -rf /confd_templates USER ${APP_RUNNER_USER} @@ -172,15 +172,16 @@ ENV APP_ENV=dev \ RUN pecl install xdebug-2.7.1; \ docker-php-ext-enable xdebug; \ # Adding the dev php.ini - mv ${PHP_INI_DIR}/php.ini-development ${PHP_INI_DIR}/php.ini - -COPY ${templates_dir}/php/conf.d/docker-php-ext-xdebug.ini.toml /etc/confd/conf.d/docker-php-ext-xdebug.ini.toml -COPY ${templates_dir}/php/templates/docker-php-ext-xdebug.ini.tmpl /etc/confd/templates/docker-php-ext-xdebug.ini.tmpl - -# Apply xdebug configurations -RUN /usr/local/bin/confd -onetime -backend env \ - # Delete xdebug configuration template files - && rm /etc/confd/conf.d/docker-php-ext-xdebug.ini.toml /etc/confd/templates/docker-php-ext-xdebug.ini.tmpl + mv ${PHP_INI_DIR}/php.ini-development ${PHP_INI_DIR}/php.ini; \ + # Copy xdebug configurations templates. + cp /confd_templates/conf.d/docker-php-ext-xdebug.ini.toml /etc/confd/conf.d/docker-php-ext-xdebug.ini.toml; \ + cp /confd_templates/templates/docker-php-ext-xdebug.ini.tmpl /etc/confd/templates/docker-php-ext-xdebug.ini.tmpl; \ + # Apply xdebug configurations. + /usr/local/bin/confd -onetime -backend env \ + # Delete xdebug configuration template files. + && rm /etc/confd/conf.d/docker-php-ext-xdebug.ini.toml /etc/confd/templates/docker-php-ext-xdebug.ini.tmpl \ + # Remove the confd templates altogether. + && rm -rf /confd_templates # Copy composer binary from official Composer image. Notice we didn't need composer for prod stage. # @TODO try to use composer_version here From 0a9e024312e8623dd324b55ee72d9ba89d6d29a4 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 15:36:53 -0400 Subject: [PATCH 017/107] Removed reference to the php and nginx base image. --- images/nginx-php/Dockerfile | 168 ------------------------------ images/nginx-php/supervisord.conf | 43 -------- 2 files changed, 211 deletions(-) delete mode 100644 images/nginx-php/Dockerfile delete mode 100644 images/nginx-php/supervisord.conf diff --git a/images/nginx-php/Dockerfile b/images/nginx-php/Dockerfile deleted file mode 100644 index f473d958d..000000000 --- a/images/nginx-php/Dockerfile +++ /dev/null @@ -1,168 +0,0 @@ -ARG php_version=7.2.27 -ARG php_pkg_release=fpm-buster - -FROM php:${php_version}-${php_pkg_release} - -ARG nginx_version=1.17.8 -ARG nginx_njs_version=0.3.8 -ARG nginx_pkg_release=1~buster - -ENV CONFD_VERSION=0.16.0 \ - CONFD_SHA256SUM="255d2559f3824dd64df059bdc533fd6b697c070db603c76aaf8d1d5e6b0cc334" \ - FILES_DIR="/mnt/files" \ - APP_ROOT="/var/www/app" \ - APP_DOCROOT="/var/www/app/web" \ - APP_RUNNER_USER=root - -# Install apt dependencies -# @TODO use specific versions of some of these to avoid version conflict or breaking -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \ - # utilitities cmd - dos2unix rsync wget findutils \ - # for imap - libc-client2007e-dev libkrb5-dev \ - # for ssh client only - openssh-client \ - # for git - git \ - # for bz2 - bzip2 libbz2-dev \ - # for gd - # libfreetype6 libfreetype6-dev libpng-tools libjpeg-dev libgmp-dev libwebp-dev \ - libfreetype6 libfreetype6-dev libpng-tools libgmp-dev libwebp-dev \ - # For image optimization - jpegoptim optipng pngquant \ - # For imagick - imagemagick libmagickwand-dev \ - # For nginx cgi-fcgi - libfcgi0ldbl \ - # for intl - libicu-dev \ - # for mcrypt - libmcrypt-dev \ - # for ldap - libldap2-dev \ - # for zip - libzip-dev zip unzip \ - # for xslt - libxslt1-dev \ - # for postgres - libpq-dev \ - # for tidy - libtidy-dev \ - # for yaml - libyaml-dev \ - # for command like drush sqlc/sqlq which need a mysql client - mariadb-client \ - # for supervsisor (http://supervisord.org/) - supervisor; \ - # install confd - wget -O /usr/local/bin/confd "https://github.com/kelseyhightower/confd/releases/download/v${CONFD_VERSION}/confd-${CONFD_VERSION}-linux-amd64"; \ - chmod +x /usr/local/bin/confd; \ - # verify confd signature - sha256sum /usr/local/bin/confd | grep -q "${CONFD_SHA256SUM}"; \ - exit $?; \ - rm -r /var/lib/apt/lists/* - -# Install and enable php extensions using the helper script provided by the base -# image -RUN docker-php-ext-configure imap --with-kerberos --with-imap-ssl \ - && docker-php-ext-configure gd --with-gd --with-webp-dir \ - --with-freetype-dir=/usr/include/freetype2 \ - --with-jpeg-dir=/usr/include/ \ - --with-png-dir=/usr/include/ \ - && docker-php-ext-configure intl --enable-intl \ - && docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu \ - && docker-php-ext-configure zip --with-libzip \ - && docker-php-ext-install -j$(nproc) \ - bcmath \ - bz2 \ - calendar \ - exif \ - gd \ - gettext \ - gmp \ - imap \ - intl \ - ldap \ - mysqli \ - opcache \ - pcntl \ - pdo_mysql \ - pdo_pgsql \ - pgsql \ - soap \ - sockets \ - tidy \ - xmlrpc \ - xsl \ - zip \ - # pecl related extensions - && pecl install \ - apcu-5.1.17 \ - imagick-3.4.4 \ - mcrypt-1.0.1 \ - yaml-2.0.4 \ - && docker-php-ext-enable \ - apcu \ - imagick \ - mcrypt \ - yaml - -# install nginx (copied from official nginx Dockerfile https://github.com/nginxinc/docker-nginx/blob/c817e28dd68b6daa33265a8cb527b1c4cd723b59/mainline/buster/Dockerfile) -ENV NGINX_VERSION=${nginx_version} \ - NGINX_PKG_RELEASE=${nginx_pkg_release} \ - NJS_VERSION=${nginx_njs_version} \ - NGINX_USER=www-data \ - NGINX_USER_GROUP=www-data - -RUN set -x \ - # Ensure nginx user/group first, to be consistent throughout docker variants - && user_exists=$(id -u ${NGINX_USER} > /dev/null 2>&1; echo $?) \ - && if [ user_exists -eq 0 ]; then \ - addgroup --system --gid 101 ${NGINX_USER_GROUP}; \ - adduser --system --disabled-login \ - --ingroup ${NGINX_USER_GROUP} --no-create-home \ - --home /nonexistent --gecos "${NGINX_USER} user" \ - --shell /bin/false --uid 101 ${NGINX_USER}; \ - fi \ - && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 \ - && \ - NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \ - found=''; \ - for server in \ - ha.pool.sks-keyservers.net \ - hkp://keyserver.ubuntu.com:80 \ - hkp://p80.pool.sks-keyservers.net:80 \ - pgp.mit.edu \ - ; do \ - echo "Fetching GPG key $NGINX_GPGKEY from $server"; \ - apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \ - done; \ - test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \ - apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \ - && nginxPackages=" \ - nginx=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ - nginx-module-xslt=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ - nginx-module-geoip=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ - nginx-module-image-filter=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ - nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-${NGINX_PKG_RELEASE} \ - " \ - # Arches officialy built by upstream - && echo "deb https://nginx.org/packages/mainline/debian/ buster nginx" >> /etc/apt/sources.list.d/nginx.list \ - && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y \ - $nginxPackages \ - gettext-base \ - && apt-get remove --purge --auto-remove -y \ - && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \ - # Create default place to store files generated by the app - && install -o ${NGINX_USER} -g ${NGINX_USER} -d "${FILES_DIR}/public" "${FILES_DIR}/private"; \ - chmod -R 775 "${FILES_DIR}" - -EXPOSE 80 443 - -# Copy custom supervisord configuration file -COPY ./supervisord.conf /etc/supervisor/conf.d/supervisord.conf -CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/conf.d/supervisord.conf"] diff --git a/images/nginx-php/supervisord.conf b/images/nginx-php/supervisord.conf deleted file mode 100644 index 2062b3792..000000000 --- a/images/nginx-php/supervisord.conf +++ /dev/null @@ -1,43 +0,0 @@ -[supervisord] -user=%(ENV_APP_RUNNER_USER)s -nodaemon=true -logfile=/dev/null -logfile_maxbytes=0 -pidfile=/var/run/supervisord.pid -loglevel = INFO - -[unix_http_server] -file=/var/run/supervisor.sock -chmod=0700 -username=docker -password=docker - -[supervisorctl] -serverurl=unix:///var/run/supervisord.sock -username=docker -password=docker - -[rpcinterface:supervisor] -supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface - -[program:php-fpm] -command = /usr/local/sbin/php-fpm -autostart=true -autorestart=true -priority=5 -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 - -[program:nginx] -command=/usr/sbin/nginx -g "daemon off;" -autostart=true -autorestart=true -priority=10 -stdout_events_enabled=true -stderr_events_enabled=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 From be78e5af981d11c213253bcd7c77ecdaf94e47f6 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 16:04:47 -0400 Subject: [PATCH 018/107] Adding print message when downloading required packages. --- scripts/drupal/init.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 96a818911..43ad70f38 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -83,9 +83,14 @@ function download_required_packages() { local version=${versions[$i]} # Only installing a package when it is not available in composer.json if [[ ! $(grep "${package}" composer.json) ]]; then + echo " Requiring ${package}. Skipping." $composer require ${package}:${version} $composer_general_flags + else + echo " ${package} was found in the composer.json. Skipping." fi done + echo " " + echo >&2 # Flag that we shouldn't run composer install to initialize everything. composer_install_run="false" From 706f6187c8070672c4c17c7451cfda3ab364753e Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 16:23:08 -0400 Subject: [PATCH 019/107] The drupal.Dockerfile support by default the islandora codebase. --- drupal.Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drupal.Dockerfile b/drupal.Dockerfile index b998134bd..cd3767617 100644 --- a/drupal.Dockerfile +++ b/drupal.Dockerfile @@ -36,6 +36,9 @@ RUN chmod 0600 /root/.ssh \ WORKDIR /app COPY ${code_dir}/composer.json ${code_dir}/composer.lock ./ +# This only work when the codebase is islandora. It should probably be commented out if installing +# the drupal/recommended-project. +COPY ${code_dir}/scripts/composer/ScriptHandler.php ./scripts/composer/ScriptHandler.php RUN set -eux; \ flags="${COMPOSER_INSTALL_FLAGS}"; \ From 261527d4b88041ba3acc1628f3ea0f23ff12d342 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 17:05:37 -0400 Subject: [PATCH 020/107] Initial workflow implementation. --- .github/docker-compose.override.yml | 4 ++++ .github/workflows/ci.yml | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 .github/docker-compose.override.yml create mode 100644 .github/workflows/ci.yml diff --git a/.github/docker-compose.override.yml b/.github/docker-compose.override.yml new file mode 100644 index 000000000..31a46bd10 --- /dev/null +++ b/.github/docker-compose.override.yml @@ -0,0 +1,4 @@ +version: "2.4" + +volumes: + mariadb-data: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..a1998028d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,20 @@ +--- +name: CI +"on": + push: + branches: + - master + - development + pull_request: + +jobs: + test: + name: Test the main docker-compose.yml default usage. + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + + steps: + - name: Checking docker version + run: docker --version From c9c9c74169477314c015d75b07a3ca5883115662 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 17:39:49 -0400 Subject: [PATCH 021/107] Checking docker and compose version and also checking out the code.. --- .github/workflows/ci.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1998028d..ab2fb8a0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,11 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest] + os: [ubuntu-latest] steps: + - name: Check out code + uses: actions/checkout@v2 + - name: Checking docker version - run: docker --version + run: docker --version && docker-compose --version From e249efd1ec33f398077cf34f248a47eef47c4839 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 18:41:00 -0400 Subject: [PATCH 022/107] Added more steps to check the home page. --- .github/docker-compose.override.yml | 8 ++++++++ .github/workflows/ci.yml | 19 +++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/docker-compose.override.yml b/.github/docker-compose.override.yml index 31a46bd10..66e263388 100644 --- a/.github/docker-compose.override.yml +++ b/.github/docker-compose.override.yml @@ -1,4 +1,12 @@ version: "2.4" +services: + drupal: + ports: + - "80:8080" + # Removing traefik functionality this way. https://github.com/docker/compose/issues/3729 + traefik: + image: hello-world + volumes: mariadb-data: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab2fb8a0f..997fad370 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,8 +16,23 @@ jobs: os: [ubuntu-latest] steps: - - name: Check out code + - name: Check out code. uses: actions/checkout@v2 - - name: Checking docker version + - name: Checking docker version. run: docker --version && docker-compose --version + + - name: Configure and run docker-compose with make. + run: | + mv .github/docker-compose.override.yml docker-compose.override.yml + make + + - name: Wait for Drupal to install. + run: | + while ! docker container logs islandora_drupal_1 | grep "nginx entered RUNNING state"; do + sleep 1 + done + + - name: Test loading the home page. + run: > + curl -s http://localhost | grep "

Welcome to islandora

" From 85d10bc2187d1e8d19cbab14d7536345e2851678 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 18:45:38 -0400 Subject: [PATCH 023/107] Fixing sed for isle settings on other distribution. --- scripts/drupal/init.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 43ad70f38..e2649a12f 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -123,15 +123,14 @@ function create_required_files() { fi fi - echo -e "\033[1m[INFO]\033[0m Setting up settings.isle.php to be included in settings.php" - echo " " - # Insert settings.isle.php snippet into the settings.php file if [[ ! -f "${default_dir}/${settings_isle}" ]]; then + echo -e "\033[1m[INFO]\033[0m Setting up settings.isle.php to be included in settings.php" + echo " " if $is_darwin; then sed -i '' -e "/${insert_after}/r ${snippet}" ${settings} else - sed -i '' -e "/${insert_after}/r ${snippet}" ${settings} + sed -i -e "/${insert_after}/r ${snippet}" ${settings} fi cp "${config_dir}/${settings_isle}" "${default_dir}/${settings_isle}" echo " settings.isle.php was successfully setup." From eeff652aec538a045a15ed05ff06b9ea4c52073b Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 18:58:01 -0400 Subject: [PATCH 024/107] Running some of the commands step by step. --- .github/workflows/ci.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 997fad370..3a7faac4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,10 +22,11 @@ jobs: - name: Checking docker version. run: docker --version && docker-compose --version - - name: Configure and run docker-compose with make. - run: | - mv .github/docker-compose.override.yml docker-compose.override.yml - make + - name: Initialize the codebase. + run: ./scripts/drupal/init.sh --codebase islandora + + - name: Run docker-compose up. + run: docker-compose -p islandora up --remove-orphans --detach - name: Wait for Drupal to install. run: | From 995a422afd1b2d36fd15e7494576db180ce08f25 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 19:14:42 -0400 Subject: [PATCH 025/107] Trying to build the image only to see why it's failing with docker compose. --- .github/.dockerignore | 14 ++++++++++++++ .github/workflows/ci.yml | 40 +++++++++++++++++++++++++++++----------- 2 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 .github/.dockerignore diff --git a/.github/.dockerignore b/.github/.dockerignore new file mode 100644 index 000000000..8b97d98d9 --- /dev/null +++ b/.github/.dockerignore @@ -0,0 +1,14 @@ +.git +.idea +.gitlab +.vscode +.gitlab-ci.yml +*/mysql +/mysql +*/files +/files +vendor/ +node_modules/ +*.sql +*.tmp +/data diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a7faac4c..56048128a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,17 +23,35 @@ jobs: run: docker --version && docker-compose --version - name: Initialize the codebase. - run: ./scripts/drupal/init.sh --codebase islandora - - - name: Run docker-compose up. - run: docker-compose -p islandora up --remove-orphans --detach - - - name: Wait for Drupal to install. run: | - while ! docker container logs islandora_drupal_1 | grep "nginx entered RUNNING state"; do - sleep 1 - done + mv .github/docker-compose.override.yml docker-compose.override.yml + mv .github/.dockerignore .dockerignore + ./scripts/drupal/init.sh --codebase islandora - - name: Test loading the home page. + - name: Build app image run: > - curl -s http://localhost | grep "

Welcome to islandora

" + docker image build + --pull + --cache-from $IMAGE_NAME_LATEST + --tag $IMAGE_NAME_TAG + --tag $IMAGE_NAME_LATEST + --target dev + --build-arg code_dir=./codebase + --build-arg templates_dir=./config + --build-arg app_bin_dir=./scripts/app_bin + --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b + --build-arg build_environment=dev + --build-arg NGINX_LISTEN_PORT=8080 + --build-arg NGINX_SERVER_ROOT=/var/www/app/web + --file drupal.Dockerfile + . + # - name: Run docker-compose up. + # run: docker-compose -p islandora up --remove-orphans --detach + # - name: Wait for Drupal to install. + # run: | + # while ! docker container logs islandora_drupal_1 | grep "nginx entered RUNNING state"; do + # sleep 1 + # done + # - name: Test loading the home page. + # run: > + # curl -s http://localhost | grep "

Welcome to islandora

" From fe47b252d5625b3748a0b1f322d8991e2a96dfbf Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 19:17:01 -0400 Subject: [PATCH 026/107] Remove unneeded args. --- .github/workflows/ci.yml | 2 -- docker-compose.yml | 2 -- drupal.Dockerfile | 4 ---- 3 files changed, 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56048128a..913698698 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,8 +37,6 @@ jobs: --tag $IMAGE_NAME_LATEST --target dev --build-arg code_dir=./codebase - --build-arg templates_dir=./config - --build-arg app_bin_dir=./scripts/app_bin --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b --build-arg build_environment=dev --build-arg NGINX_LISTEN_PORT=8080 diff --git a/docker-compose.yml b/docker-compose.yml index ffe2aa065..8ee77cbd3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,8 +25,6 @@ services: target: dev args: - code_dir=./codebase - - templates_dir=./config - - app_bin_dir=./scripts/app_bin - base_image_tag=7.2.28-1.17.8-0ceedc1b - build_environment=dev # XDEBUG confd build time args - you can set it to 0 turn it off. diff --git a/drupal.Dockerfile b/drupal.Dockerfile index cd3767617..5b572df1a 100644 --- a/drupal.Dockerfile +++ b/drupal.Dockerfile @@ -2,7 +2,6 @@ ARG build_environment=prod ARG code_dir=./codebase ARG base_image_tag=7.2.28-1.17.8-0ceedc1b ARG composer_version=1.9.3 -ARG templates_dir=./config # # Stage 1: PHP Dependencies @@ -62,12 +61,10 @@ RUN set -eux; \ # FROM registry.gitlab.com/nikathone/drupal-docker-good-defaults/php-nginx:${base_image_tag} as base ARG code_dir -ARG templates_dir ARG app_runner_user=drupal ARG app_runner_user_id=1000 ARG app_runner_group=drupal ARG app_runner_group_id=1000 -ARG app_bin_dir=./scripts/app_bin ARG NGINX_LISTEN_PORT=8080 ARG NGINX_SERVER_ROOT @@ -161,7 +158,6 @@ USER ${APP_RUNNER_USER} # FROM base AS dev ARG composer_version -ARG templates_dir ARG PHP_XDEBUG ARG PHP_XDEBUG_DEFAULT_ENABLE ARG PHP_XDEBUG_REMOTE_CONNECT_BACK From edc97981b742f9767f3cd55bdbd3904b267d83d8 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 19:37:54 -0400 Subject: [PATCH 027/107] Wrong tag name. --- .github/workflows/ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 913698698..7c3107ee9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,10 +31,7 @@ jobs: - name: Build app image run: > docker image build - --pull - --cache-from $IMAGE_NAME_LATEST - --tag $IMAGE_NAME_TAG - --tag $IMAGE_NAME_LATEST + --tag app-image-test:latest --target dev --build-arg code_dir=./codebase --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b From a98676a233b9ee801a2a8f56a5fa5a477e0ccdf4 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 00:31:43 -0400 Subject: [PATCH 028/107] Adding gitlab-ci for testing. --- .gitlab-ci.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..619b3da90 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,25 @@ +variables: + DOCKER_VERSION: "19.03" + +test: + image: registry.gitlab.com/nikathone/image-utils/docker:$DOCKER_VERSION-compose + stage: test + services: + - registry.gitlab.com/nikathone/image-utils/docker:$DOCKER_VERSION-dind + before_script: + - docker --version && docker-compose --version + script: + - | + mv .github/docker-compose.override.yml docker-compose.override.yml + mv .github/.dockerignore .dockerignore + ./scripts/drupal/init.sh --codebase islandora + - > + docker image build + --tag app-image-test:latest + --target dev + --build-arg code_dir=./codebase + --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b + --build-arg build_environment=dev + --build-arg NGINX_LISTEN_PORT=8080 + --build-arg NGINX_SERVER_ROOT=/var/www/app/web + --file drupal.Dockerfile From c89f39d9a05c3a3612f2fbadd20e883e2854393c Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 00:43:19 -0400 Subject: [PATCH 029/107] Trying with official docker dind. --- .gitlab-ci.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 619b3da90..db2f8cbbf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,11 +1,14 @@ variables: - DOCKER_VERSION: "19.03" + DOCKER_VERSION: "19.03.8" test: image: registry.gitlab.com/nikathone/image-utils/docker:$DOCKER_VERSION-compose stage: test + # variables: + # DOCKER_DRIVER: overlay2 + # DOCKER_TLS_CERTDIR: "/certs" services: - - registry.gitlab.com/nikathone/image-utils/docker:$DOCKER_VERSION-dind + - docker:$DOCKER_VERSION-dind before_script: - docker --version && docker-compose --version script: @@ -23,3 +26,7 @@ test: --build-arg NGINX_LISTEN_PORT=8080 --build-arg NGINX_SERVER_ROOT=/var/www/app/web --file drupal.Dockerfile + only: + refs: + - master + - development From c3b9254f4e67638ff69541215725f76687ebf7fa Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 00:47:03 -0400 Subject: [PATCH 030/107] Remove multiline. --- .gitlab-ci.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index db2f8cbbf..b405f53af 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,10 +12,9 @@ test: before_script: - docker --version && docker-compose --version script: - - | - mv .github/docker-compose.override.yml docker-compose.override.yml - mv .github/.dockerignore .dockerignore - ./scripts/drupal/init.sh --codebase islandora + - mv .github/docker-compose.override.yml docker-compose.override.yml + - mv .github/.dockerignore .dockerignore + - ./scripts/drupal/init.sh --codebase islandora - > docker image build --tag app-image-test:latest From b5834b043669d68b8e3cae0c63e6676a5ff0ba0a Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 01:01:12 -0400 Subject: [PATCH 031/107] Only running mov commands. --- .gitlab-ci.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b405f53af..ec93cf9ce 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,17 +14,17 @@ test: script: - mv .github/docker-compose.override.yml docker-compose.override.yml - mv .github/.dockerignore .dockerignore - - ./scripts/drupal/init.sh --codebase islandora - - > - docker image build - --tag app-image-test:latest - --target dev - --build-arg code_dir=./codebase - --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b - --build-arg build_environment=dev - --build-arg NGINX_LISTEN_PORT=8080 - --build-arg NGINX_SERVER_ROOT=/var/www/app/web - --file drupal.Dockerfile + # - ./scripts/drupal/init.sh --codebase islandora + # - > + # docker image build + # --tag app-image-test:latest + # --target dev + # --build-arg code_dir=./codebase + # --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b + # --build-arg build_environment=dev + # --build-arg NGINX_LISTEN_PORT=8080 + # --build-arg NGINX_SERVER_ROOT=/var/www/app/web + # --file drupal.Dockerfile only: refs: - master From 5ca71d61c170f14ef6e39bb4b09aad6b68c32b64 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 11:11:49 -0400 Subject: [PATCH 032/107] Making sure init.sh is executable. --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ec93cf9ce..356c16276 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,6 +14,7 @@ test: script: - mv .github/docker-compose.override.yml docker-compose.override.yml - mv .github/.dockerignore .dockerignore + - chmod +x scripts/drupal/init.sh # - ./scripts/drupal/init.sh --codebase islandora # - > # docker image build From 570bbb1b0baa799e8e325db13984251ee1f2c571 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 11:35:07 -0400 Subject: [PATCH 033/107] Making sure I am delaing with ubuntu. --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 356c16276..826c8761b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,6 +15,8 @@ test: - mv .github/docker-compose.override.yml docker-compose.override.yml - mv .github/.dockerignore .dockerignore - chmod +x scripts/drupal/init.sh + - echo "$(uname -s)" + - if [[ -f /etc/os-release ]]; then cat /etc/os-release; fi # - ./scripts/drupal/init.sh --codebase islandora # - > # docker image build From c404e6920aba5b7db4115dbf714feff12e92cef9 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 12:14:10 -0400 Subject: [PATCH 034/107] Checking files permissions in Gitlab CI. --- .gitlab-ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 826c8761b..02308ad0e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,9 +14,8 @@ test: script: - mv .github/docker-compose.override.yml docker-compose.override.yml - mv .github/.dockerignore .dockerignore + - ls -all scripts/drupal/ - chmod +x scripts/drupal/init.sh - - echo "$(uname -s)" - - if [[ -f /etc/os-release ]]; then cat /etc/os-release; fi # - ./scripts/drupal/init.sh --codebase islandora # - > # docker image build @@ -28,6 +27,9 @@ test: # --build-arg NGINX_LISTEN_PORT=8080 # --build-arg NGINX_SERVER_ROOT=/var/www/app/web # --file drupal.Dockerfile + after_script: + - echo "$(uname -s)" + - if [[ -f /etc/os-release ]]; then cat /etc/os-release; fi only: refs: - master From ea1e7415f2098f31ae868b6eab785c960cc36a7d Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 12:23:24 -0400 Subject: [PATCH 035/107] Making sure that checking composer won't break the CI run in gitlab. --- .gitlab-ci.yml | 2 +- scripts/drupal/init.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 02308ad0e..8610dc027 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,7 +16,7 @@ test: - mv .github/.dockerignore .dockerignore - ls -all scripts/drupal/ - chmod +x scripts/drupal/init.sh - # - ./scripts/drupal/init.sh --codebase islandora + - scripts/drupal/init.sh --codebase islandora # - > # docker image build # --tag app-image-test:latest diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index e2649a12f..45cd24eda 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -162,7 +162,7 @@ done ### # Determine how we will be running composer. ### -composer=$(command -v composer) +composer=$(command -v composer || true) if [[ ! $composer ]]; then # We use the docker composer image to run composer related commands. echo >&2 From cb2a169a7e0ad723e2866d2f793f5505b65f0007 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 12:32:24 -0400 Subject: [PATCH 036/107] Replaced the HOME environment variable with tilda. --- .gitlab-ci.yml | 1 + scripts/drupal/init.sh | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8610dc027..a70a2436d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,6 +15,7 @@ test: - mv .github/docker-compose.override.yml docker-compose.override.yml - mv .github/.dockerignore .dockerignore - ls -all scripts/drupal/ + - echo "$HOME" - chmod +x scripts/drupal/init.sh - scripts/drupal/init.sh --codebase islandora # - > diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 45cd24eda..a1480b474 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -169,7 +169,8 @@ if [[ ! $composer ]]; then echo -e "\033[1m[INFO]\033[0m Using the official composer docker image to run composer commands" echo " " echo >&2 - composer="mkdir -p $HOME/.composer && docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" + mkdir -p ~/.composer + composer="docker container run -it --rm -v ~/.composer:/tmp -v $PWD:/app composer:1.9.3" fi ### From f82c70e479242f4280c1afa5dc9ff4afcf8aeb15 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 13:28:33 -0400 Subject: [PATCH 037/107] Removed the interactivity of the container for none Darwin system. --- scripts/drupal/init.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index a1480b474..461431543 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -170,7 +170,11 @@ if [[ ! $composer ]]; then echo " " echo >&2 mkdir -p ~/.composer - composer="docker container run -it --rm -v ~/.composer:/tmp -v $PWD:/app composer:1.9.3" + if $is_darwin; then + composer="docker container run -it --rm -v ~/.composer:/tmp -v $PWD:/app composer:1.9.3" + else + composer="docker container run -t --rm -v ~/.composer:/tmp -v $PWD:/app composer:1.9.3" + fi fi ### From 794add27914887311f6b857e84523815f35a8274 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 13:35:21 -0400 Subject: [PATCH 038/107] Removed the interactivity of the container for none Darwin system. --- scripts/drupal/init.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 461431543..68dc9f42f 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -169,11 +169,11 @@ if [[ ! $composer ]]; then echo -e "\033[1m[INFO]\033[0m Using the official composer docker image to run composer commands" echo " " echo >&2 - mkdir -p ~/.composer + mkdir -p "$HOME/.composer" if $is_darwin; then - composer="docker container run -it --rm -v ~/.composer:/tmp -v $PWD:/app composer:1.9.3" + composer="docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" else - composer="docker container run -t --rm -v ~/.composer:/tmp -v $PWD:/app composer:1.9.3" + composer="docker container run -t -- rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" fi fi From 48e4b634be876244c1e35f6a975664e1e931bfb1 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 14:50:21 -0400 Subject: [PATCH 039/107] A typo for the remove flag. --- scripts/drupal/init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 68dc9f42f..268834d74 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -173,7 +173,7 @@ if [[ ! $composer ]]; then if $is_darwin; then composer="docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" else - composer="docker container run -t -- rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" + composer="docker container run -t --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" fi fi From 95af8f00880ab19f7eb9dd4899d6c6c35b0dab42 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 16:57:06 -0400 Subject: [PATCH 040/107] Running make in the gitlab CI. --- .github/docker-compose.override.yml | 3 ++- .gitlab-ci.yml | 18 +++++++----------- Makefile | 2 -- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/.github/docker-compose.override.yml b/.github/docker-compose.override.yml index 66e263388..3949ae8d8 100644 --- a/.github/docker-compose.override.yml +++ b/.github/docker-compose.override.yml @@ -4,7 +4,8 @@ services: drupal: ports: - "80:8080" - # Removing traefik functionality this way. https://github.com/docker/compose/issues/3729 + # Removing traefik service functionality cause we don't need it during testing. + # https://github.com/docker/compose/issues/3729 traefik: image: hello-world diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a70a2436d..ac1f1b911 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,17 +17,13 @@ test: - ls -all scripts/drupal/ - echo "$HOME" - chmod +x scripts/drupal/init.sh - - scripts/drupal/init.sh --codebase islandora - # - > - # docker image build - # --tag app-image-test:latest - # --target dev - # --build-arg code_dir=./codebase - # --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b - # --build-arg build_environment=dev - # --build-arg NGINX_LISTEN_PORT=8080 - # --build-arg NGINX_SERVER_ROOT=/var/www/app/web - # --file drupal.Dockerfile + - make + - > + while ! docker container logs islandora_drupal_1 | grep "nginx entered RUNNING state"; do + echo "Waiting for the site to be installed!" + sleep 2m + done + - curl -s http://localhost:8000 | grep '

Welcome to islandora

' after_script: - echo "$(uname -s)" - if [[ -f /etc/os-release ]]; then cat /etc/os-release; fi diff --git a/Makefile b/Makefile index b5f532419..c942fc80d 100644 --- a/Makefile +++ b/Makefile @@ -38,8 +38,6 @@ down_rmi_local: --volumes \ --remove-orphans -# @todo we might chmod here since drupal set settings.php to readonly after -# install drupal_clean: chmod u+w codebase/web/sites/default && rm -rf codebase data/drupal From 6dbc203075565924e2f4bc64b9d6a86f2b3b5f85 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 20:00:49 -0400 Subject: [PATCH 041/107] Using drupal-composer/drupal-project for drupal codebase and try a simple build image in the CI. --- .gitlab-ci.yml | 26 +++++++++++++++++--------- scripts/drupal/init.sh | 3 ++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ac1f1b911..b1d677521 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,9 +4,6 @@ variables: test: image: registry.gitlab.com/nikathone/image-utils/docker:$DOCKER_VERSION-compose stage: test - # variables: - # DOCKER_DRIVER: overlay2 - # DOCKER_TLS_CERTDIR: "/certs" services: - docker:$DOCKER_VERSION-dind before_script: @@ -17,13 +14,24 @@ test: - ls -all scripts/drupal/ - echo "$HOME" - chmod +x scripts/drupal/init.sh - - make - > - while ! docker container logs islandora_drupal_1 | grep "nginx entered RUNNING state"; do - echo "Waiting for the site to be installed!" - sleep 2m - done - - curl -s http://localhost:8000 | grep '

Welcome to islandora

' + docker image build + --tag app-image-test:latest + --target dev + --build-arg code_dir=./codebase + --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b + --build-arg build_environment=dev + --build-arg NGINX_LISTEN_PORT=8080 + --build-arg NGINX_SERVER_ROOT=/var/www/app/web + --file drupal.Dockerfile + . + # - make + # - > + # while ! docker container logs islandora_drupal_1 | grep "nginx entered RUNNING state"; do + # echo "Waiting for the site to be installed!" + # sleep 2m + # done + # - curl -s http://localhost | grep '

Welcome to islandora

' after_script: - echo "$(uname -s)" - if [[ -f /etc/os-release ]]; then cat /etc/os-release; fi diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 268834d74..2980bd45a 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -29,7 +29,8 @@ function help() { } function download_drupal() { - local args="create-project drupal/recommended-project:^8.8" + local args="create-project drupal-composer/drupal-project:8.x-dev" + # local args="create-project drupal/recommended-project:^8.8" local flags="--ignore-platform-reqs --no-interaction" local codebase="$1" local drush_require="require drush/drush $flags" From b36b15377b2269c0d460eec07b780ef88d8c4349 Mon Sep 17 00:00:00 2001 From: "Noah W. Smith" Date: Thu, 19 Mar 2020 17:27:33 -0400 Subject: [PATCH 042/107] adding Solr back in; adding default Islandora config --- Makefile | 5 ++++- docker-compose.yml | 28 ++++++++++++++-------------- scripts/drupal/composer.json | 2 +- scripts/drupal/init.sh | 11 ++++++++++- scripts/solr/create-core.sh | 1 + 5 files changed, 30 insertions(+), 17 deletions(-) create mode 100755 scripts/solr/create-core.sh diff --git a/Makefile b/Makefile index c942fc80d..06369ab03 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ docker_compose_project ?= islandora .PHONY: help drupal_init up build down down_rmi_all down_rmi_local drupal_clean clean_local clean -default: drupal_init up +default: drupal_init solr_init up help: ./scripts/drupal/init.sh --help @@ -14,6 +14,9 @@ help: drupal_init: ./scripts/drupal/init.sh --codebase $(isle_codebase) +solr_init: + ./scripts/solr/create-core.sh + up: docker-compose -p $(docker_compose_project) up --remove-orphans --detach diff --git a/docker-compose.yml b/docker-compose.yml index 8ee77cbd3..94d90978a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -76,19 +76,19 @@ services: - "traefik.http.routers.${APP_NAME:-islandora}.rule=Host(`${DRUPAL_LOCAL_URL:-islandora.localhost}`)" - "traefik.http.services.${APP_NAME:-islandora}.loadbalancer.server.port=8080" - # solr: - # image: wodby/solr:$SOLR_TAG - # container_name: "${PROJECT_NAME}_solr" - # networks: - # - internal - # environment: - # SOLR_DEFAULT_CONFIG_SET: $SOLR_CONFIG_SET - # SOLR_HEAP: 1024m - # volumes: - # - solr-data:/opt/solr/server/solr - # # TO DO: Determine what type of container handling is needed - # labels: - # - "traefik.http.routers.${PROJECT_NAME}_solr.rule=Host(`solr.${PROJECT_BASE_URL}`)" + solr: + image: wodby/solr:$SOLR_TAG + container_name: "${PROJECT_NAME}_solr" + # networks: + # - internal + environment: + SOLR_DEFAULT_CONFIG_SET: $SOLR_CONFIG_SET + SOLR_HEAP: 1024m + volumes: + - solr-data:/opt/solr/server/solr + # TO DO: Determine what type of container handling is needed + labels: + - "traefik.http.routers.${PROJECT_NAME}_solr.rule=Host(`solr.${PROJECT_BASE_URL}`)" traefik: # review https://hub.docker.com/_/traefik @@ -125,5 +125,5 @@ volumes: type: none o: bind device: ${PWD}/data/drupal/database - # solr-data: + solr-data: # isle-dc-postgres-data # Added to prototype but not currently used diff --git a/scripts/drupal/composer.json b/scripts/drupal/composer.json index e8208ad8d..4efa445bc 100644 --- a/scripts/drupal/composer.json +++ b/scripts/drupal/composer.json @@ -22,7 +22,7 @@ "drupal/core-recommended": "8.8.1", "drupal/search_api": "^1.1", "drupal/search_api_solr": "^3", - "drupal/search_api_solr_defaults": "^3.9", + "drupal/search_api_solr_defaults": "^3.9", "drupal/search_api_solr_admin": "^3.9" }, "require-dev": { diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 2980bd45a..95339e78a 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -40,7 +40,7 @@ function download_drupal() { fi if [[ "$codebase" == "islandora" ]]; then - local args="create-project islandora/drupal-project" + local args="create-project born-digital/drupal-project:dev-isle8-dev" fi echo -e "\033[1m[INFO]\033[0m Installing drupal using composer" @@ -187,6 +187,15 @@ else download_required_packages fi + + +# load the config here +if [[ "$codebase" == "islandora" ]]; then + if [[ ! "$(ls -A codebase/config/sync)" && ! -f codebase/config/sync/core.extension.yml ]]; then + tar -xzf config/drupal/islandora-starter-config.tar.gz codebase/config/sync + fi +fi + ### # Initialize drupal database and files persistent storage folders. ### diff --git a/scripts/solr/create-core.sh b/scripts/solr/create-core.sh new file mode 100755 index 000000000..d0348519e --- /dev/null +++ b/scripts/solr/create-core.sh @@ -0,0 +1 @@ +docker-compose -p islandora exec solr /opt/solr/bin/solr create -c islandora From 7db310bdcc55b4249f6b381b2cf5e6dd300f1925 Mon Sep 17 00:00:00 2001 From: nikathone Date: Mon, 24 Feb 2020 13:41:52 -0500 Subject: [PATCH 043/107] Added config files related to drupal, nginx, php and supervisord. --- config/drupal/confd/conf.d/app.env.toml | 3 + config/drupal/confd/templates/app.env.tmpl | 24 + config/nginx/conf.d/nginx.conf.toml | 3 + config/nginx/conf.d/vhost.conf.toml | 3 + config/nginx/templates/nginx.conf.tmpl | 44 ++ config/nginx/templates/vhost.conf.tmpl | 78 ++++ .../php/conf.d/docker-php-ext-apcu.ini.toml | 3 + .../conf.d/docker-php-ext-opcache.ini.toml | 3 + .../php/conf.d/docker-php-ext-xdebug.ini.toml | 3 + config/php/conf.d/docker-php.ini.toml | 3 + config/php/conf.d/zz-www.conf.toml | 3 + .../templates/docker-php-ext-apcu.ini.tmpl | 4 + .../templates/docker-php-ext-opcache.ini.tmpl | 9 + .../templates/docker-php-ext-xdebug.ini.tmpl | 26 ++ config/php/templates/docker-php.ini.tmpl | 422 ++++++++++++++++++ config/php/templates/zz-www.conf.tmpl | 70 +++ config/supervisord/nginx_php/supervisord.conf | 43 ++ 17 files changed, 744 insertions(+) create mode 100644 config/drupal/confd/conf.d/app.env.toml create mode 100644 config/drupal/confd/templates/app.env.tmpl create mode 100644 config/nginx/conf.d/nginx.conf.toml create mode 100644 config/nginx/conf.d/vhost.conf.toml create mode 100644 config/nginx/templates/nginx.conf.tmpl create mode 100644 config/nginx/templates/vhost.conf.tmpl create mode 100644 config/php/conf.d/docker-php-ext-apcu.ini.toml create mode 100644 config/php/conf.d/docker-php-ext-opcache.ini.toml create mode 100644 config/php/conf.d/docker-php-ext-xdebug.ini.toml create mode 100644 config/php/conf.d/docker-php.ini.toml create mode 100644 config/php/conf.d/zz-www.conf.toml create mode 100644 config/php/templates/docker-php-ext-apcu.ini.tmpl create mode 100644 config/php/templates/docker-php-ext-opcache.ini.tmpl create mode 100644 config/php/templates/docker-php-ext-xdebug.ini.tmpl create mode 100644 config/php/templates/docker-php.ini.tmpl create mode 100644 config/php/templates/zz-www.conf.tmpl create mode 100644 config/supervisord/nginx_php/supervisord.conf diff --git a/config/drupal/confd/conf.d/app.env.toml b/config/drupal/confd/conf.d/app.env.toml new file mode 100644 index 000000000..77413f359 --- /dev/null +++ b/config/drupal/confd/conf.d/app.env.toml @@ -0,0 +1,3 @@ +[template] +src = "app.env.tmpl" +dest = "/var/www/app/.env" diff --git a/config/drupal/confd/templates/app.env.tmpl b/config/drupal/confd/templates/app.env.tmpl new file mode 100644 index 000000000..3235d2261 --- /dev/null +++ b/config/drupal/confd/templates/app.env.tmpl @@ -0,0 +1,24 @@ +### APP SETTINGS + +APP_NAME={{ getenv "APP_NAME" "drupal" }} +APP_TITLE={{ getenv "APP_TITLE" "drupal" }} +APP_ACCOUNT_NAME={{ getenv "APP_ACCOUNT_NAME" "admin" }} +APP_ACCOUNT_MAIL={{ getenv "APP_ACCOUNT_MAIL" "drupal@example.com" }} +APP_ENV={{ getenv "APP_ENV" "local" }} + +DB_NAME={{ getenv "DB_NAME" "drupal" }} +DB_USER={{ getenv "DB_USER" "drupal" }} +DB_PASSWORD={{ getenv "DB_PASSWORD" "dbpassword" }} +DB_HOST={{ getenv "DB_HOST" "mariadb" }} +DB_DRIVER={{ getenv "DB_DRIVER" "mysql" }} +DB_PORT={{ getenv "DB_PORT" "3306" }} + +### Stack SETTINGS + +APP_ROOT={{ getenv "APP_ROOT" "/var/www/app" }} +APP_DOCROOT={{ getenv "APP_DOCROOT" "/var/www/app/web" }} +FILES_DIR={{ getenv "FILES_DIR" "/mnt/files" }} + +### Drush + +DRUSH_OPTIONS_URI={{ getenv "DRUSH_OPTIONS_URI" "" }} diff --git a/config/nginx/conf.d/nginx.conf.toml b/config/nginx/conf.d/nginx.conf.toml new file mode 100644 index 000000000..3e968ea8b --- /dev/null +++ b/config/nginx/conf.d/nginx.conf.toml @@ -0,0 +1,3 @@ +[template] +src = "nginx.conf.tmpl" +dest = "/etc/nginx/nginx.conf" diff --git a/config/nginx/conf.d/vhost.conf.toml b/config/nginx/conf.d/vhost.conf.toml new file mode 100644 index 000000000..f7d52405e --- /dev/null +++ b/config/nginx/conf.d/vhost.conf.toml @@ -0,0 +1,3 @@ +[template] +src = "vhost.conf.tmpl" +dest = "/etc/nginx/conf.d/default.conf" diff --git a/config/nginx/templates/nginx.conf.tmpl b/config/nginx/templates/nginx.conf.tmpl new file mode 100644 index 000000000..0e393fc8e --- /dev/null +++ b/config/nginx/templates/nginx.conf.tmpl @@ -0,0 +1,44 @@ +user {{ getenv "NGINX_USER" "www-data" }}; + +worker_processes {{ getenv "NGINX_WORKER_PROCESSES" "auto" }}; +error_log /var/log/nginx/error.log warn; +pid /var/run/nginx.pid; + +events { + worker_connections {{ getenv "NGINX_WORKER_CONNECTIONS" "1024" }}; + multi_accept {{ getenv "NGINX_MULTI_ACCEPT" "on" }}; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + proxy_temp_path /tmp/proxy_temp; + client_body_temp_path /tmp/client_temp; + fastcgi_temp_path /tmp/fastcgi_temp; + uwsgi_temp_path /tmp/uwsgi_temp; + scgi_temp_path /tmp/scgi_temp; + + server_names_hash_bucket_size {{ getenv "NGINX_SERVER_NAMES_HASH_BUCKET_SIZE" "64" }}; + + client_max_body_size {{ getenv "NGINX_CLIENT_MAX_BODY_SIZE" "64m" }}; + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile {{ getenv "NGINX_SENDFILE" "on" }}; + tcp_nopush {{ getenv "NGINX_TCP_NOPUSH" "on" }}; + tcp_nodelay {{ getenv "NGINX_TCP_NODELAY" "on" }}; + types_hash_max_size {{ getenv "NGINX_TYPES_HASH_MAX_SIZE" "2048" }}; + + keepalive_timeout {{ getenv "NGINX_KEEPALIVE_TIMEOUT" "75s" }}; + keepalive_requests {{ getenv "NGINX_KEEPALIVE_REQUESTS" "100" }}; + + server_tokens {{ getenv "NGINX_SERVER_TOKENS" "off" }}; + + gzip {{ getenv "NGINX_GZIP" "on" }}; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/config/nginx/templates/vhost.conf.tmpl b/config/nginx/templates/vhost.conf.tmpl new file mode 100644 index 000000000..c9ae55f05 --- /dev/null +++ b/config/nginx/templates/vhost.conf.tmpl @@ -0,0 +1,78 @@ +server { + listen {{ getenv "NGINX_LISTEN_PORT" "80" }} default_server{{ if getenv "NGINX_HTTP2" }} http2{{ end }}; + server_name {{ getenv "NGINX_SERVER_NAME" "default" }}; + + root {{ getenv "NGINX_SERVER_ROOT" "/var/www/app/web" }}; + index index.php index.html; + + location = /favicon.ico { + log_not_found off; + access_log {{ getenv "NGINX_STATIC_ACCESS_LOG" "off" }}; + } + + location = /robots.txt { + allow all; + log_not_found off; + access_log {{ getenv "NGINX_STATIC_ACCESS_LOG" "off" }}; + } + + location ~ \..*/.*\.php$ { + return 403; + } + + location ~ ^/sites/.*/private/ { + return 403; + } + + # Block access to scripts in site files directory + location ~ ^/sites/[^/]+/files/.*\.php$ { + deny all; + } + + # Allow "Well-Known URIs" as per RFC 5785 + location ~* ^/.well-known/ { + allow all; + } + + # Block access to "hidden" files and directories whose names begin with a + # period. This includes directories used by version control systems such + # as Subversion or Git to store control files. + location ~ (^|/)\. { + return 403; + } + + location / { + try_files $uri /index.php?$query_string; + } + + location @rewrite { + rewrite ^/(.*)$ /index.php?q=$1; + } + + location ~ '\.php$|^/update.php' { + fastcgi_split_path_info ^(.+?\.php)(|/.*)$; + include fastcgi_params; + # Block httpoxy attacks. See https://httpoxy.org/. + fastcgi_param HTTP_PROXY ""; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_param PATH_INFO $fastcgi_path_info; + fastcgi_param QUERY_STRING $query_string; + fastcgi_intercept_errors {{ getenv "NGINX_FASTCGI_INTERCEPT_ERRORS" "on" }}; + fastcgi_pass unix:/var/run/php-fpm.sock; + } + + # Fighting with Styles? This little gem is amazing. + location ~ ^/sites/.*/files/styles/ { + try_files $uri @rewrite; + } + + # Handle private files through Drupal. Private file's path can come + # with a language prefix. + location ~ ^(/[a-z\-]+)?/system/files/ { + try_files $uri /index.php?$query_string; + } + + location ~* ^(?:.+\.(?:htaccess|make|txt|engine|inc|info|install|module|profile|po|pot|sh|.*sql|test|theme|tpl(?:\.php)?|xtmpl)|code-style\.pl|/Entries.*|/Repository|/Root|/Tag|/Template)$ { + return 404; + } +} diff --git a/config/php/conf.d/docker-php-ext-apcu.ini.toml b/config/php/conf.d/docker-php-ext-apcu.ini.toml new file mode 100644 index 000000000..9cabe33fe --- /dev/null +++ b/config/php/conf.d/docker-php-ext-apcu.ini.toml @@ -0,0 +1,3 @@ +[template] +src = "docker-php-ext-apcu.ini.tmpl" +dest = "/usr/local/etc/php/conf.d/docker-php-ext-apcu.ini" diff --git a/config/php/conf.d/docker-php-ext-opcache.ini.toml b/config/php/conf.d/docker-php-ext-opcache.ini.toml new file mode 100644 index 000000000..59c27c8ae --- /dev/null +++ b/config/php/conf.d/docker-php-ext-opcache.ini.toml @@ -0,0 +1,3 @@ +[template] +src = "docker-php-ext-opcache.ini.tmpl" +dest = "/usr/local/etc/php/conf.d/docker-php-ext-opcache.ini" diff --git a/config/php/conf.d/docker-php-ext-xdebug.ini.toml b/config/php/conf.d/docker-php-ext-xdebug.ini.toml new file mode 100644 index 000000000..f10c1d7f3 --- /dev/null +++ b/config/php/conf.d/docker-php-ext-xdebug.ini.toml @@ -0,0 +1,3 @@ +[template] +src = "docker-php-ext-xdebug.ini.tmpl" +dest = "/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini" diff --git a/config/php/conf.d/docker-php.ini.toml b/config/php/conf.d/docker-php.ini.toml new file mode 100644 index 000000000..22ab90dd7 --- /dev/null +++ b/config/php/conf.d/docker-php.ini.toml @@ -0,0 +1,3 @@ +[template] +src = "docker-php.ini.tmpl" +dest = "/usr/local/etc/php/conf.d/docker-php.ini" diff --git a/config/php/conf.d/zz-www.conf.toml b/config/php/conf.d/zz-www.conf.toml new file mode 100644 index 000000000..690939af5 --- /dev/null +++ b/config/php/conf.d/zz-www.conf.toml @@ -0,0 +1,3 @@ +[template] +src = "zz-www.conf.tmpl" +dest = "/usr/local/etc/php-fpm.d/zz-www.conf" diff --git a/config/php/templates/docker-php-ext-apcu.ini.tmpl b/config/php/templates/docker-php-ext-apcu.ini.tmpl new file mode 100644 index 000000000..f406016f1 --- /dev/null +++ b/config/php/templates/docker-php-ext-apcu.ini.tmpl @@ -0,0 +1,4 @@ +[apcu] +extension=apcu.so +apc.shm_size = {{ getenv "PHP_APCU_SHM_SIZE" "128M" }} +apc.enable_cli = {{ getenv "PHP_APCU_ENABLE_CLI" "Off" }} diff --git a/config/php/templates/docker-php-ext-opcache.ini.tmpl b/config/php/templates/docker-php-ext-opcache.ini.tmpl new file mode 100644 index 000000000..b132edbfa --- /dev/null +++ b/config/php/templates/docker-php-ext-opcache.ini.tmpl @@ -0,0 +1,9 @@ +[opcache] +zend_extension = opcache.so +opcache.enable = {{ getenv "PHP_OPCACHE_ENABLE" "On" }} +opcache.enable_cli = {{ getenv "PHP_OPCACHE_ENABLE_CLI" "Off" }} +opcache.memory_consumption = {{ getenv "PHP_OPCACHE_MEMORY_CONSUMPTION" "128" }} +opcache.interned_strings_buffer = {{ getenv "PHP_OPCACHE_INTERNED_STRINGS_BUFFER" "8" }} +opcache.max_accelerated_files = {{ getenv "PHP_OPCACHE_MAX_ACCELERATED_FILES" "4096" }} +opcache.validate_timestamps = {{ getenv "PHP_OPCACHE_VALIDATE_TIMESTAMPS" "On" }} +opcache.revalidate_freq = {{ getenv "PHP_OPCACHE_REVALIDATE_FREQ" "2" }} diff --git a/config/php/templates/docker-php-ext-xdebug.ini.tmpl b/config/php/templates/docker-php-ext-xdebug.ini.tmpl new file mode 100644 index 000000000..ab4692c38 --- /dev/null +++ b/config/php/templates/docker-php-ext-xdebug.ini.tmpl @@ -0,0 +1,26 @@ +{{ if getenv "PHP_XDEBUG" }} +[xdebug] +zend_extension = xdebug.so + +xdebug.coverage_enable = {{ getenv "PHP_XDEBUG_COVERAGE_ENABLE" "On" }} +xdebug.default_enable = {{ getenv "PHP_XDEBUG_DEFAULT_ENABLE" "On" }} + +xdebug.remote_enable = {{ getenv "PHP_XDEBUG_REMOTE_ENABLE" "On" }} +xdebug.remote_handler = {{ getenv "PHP_XDEBUG_REMOTE_HANDLER" "dbgp" }} +xdebug.remote_connect_back = {{ getenv "PHP_XDEBUG_REMOTE_CONNECT_BACK" "On" }} +xdebug.remote_host = {{ getenv "PHP_XDEBUG_REMOTE_HOST" "localhost" }} +xdebug.remote_port = {{ getenv "PHP_XDEBUG_REMOTE_PORT" "9000" }} +xdebug.remote_log = "{{ getenv "PHP_XDEBUG_REMOTE_LOG" "" }}" +xdebug.remote_autostart = {{ getenv "PHP_XDEBUG_REMOTE_AUTOSTART" "On" }} + +xdebug.profiler_enable = {{ getenv "PHP_XDEBUG_PROFILER_ENABLE" "Off" }} +xdebug.profiler_enable_trigger = {{ getenv "PHP_XDEBUG_PROFILER_ENABLE_TRIGGER" "On" }} +xdebug.profiler_enable_trigger_value = "{{ getenv "PHP_XDEBUG_PROFILER_ENABLE_TRIGGER_VALUE" "XDEBUG_PROFILE" }}" +xdebug.profiler_output_dir = {{ getenv "FILES_DIR" }}/xdebug/profiler +xdebug.profiler_output_name = {{ getenv "PHP_XDEBUG_PROFILER_OUTPUT_NAME" "cachegrind.out.%p" }} + +xdebug.idekey = {{ getenv "PHP_XDEBUG_IDEKEY" "PHPSTORM"}} + +xdebug.max_nesting_level = {{ getenv "PHP_XDEBUG_MAX_NESTING_LEVEL" "256" }} + +{{ end }} diff --git a/config/php/templates/docker-php.ini.tmpl b/config/php/templates/docker-php.ini.tmpl new file mode 100644 index 000000000..97f38521c --- /dev/null +++ b/config/php/templates/docker-php.ini.tmpl @@ -0,0 +1,422 @@ +; This file is used to override the default php.ini values +; BASIC SETTINGS: $PHP_INI_DIR/php.ini +; PHP-FPM SETTINGS: /usr/local/etc/php-fpm.d/zz-www.conf + +[PHP] + +;;;;;;;;;;;;;;;;;;;; +; Language Options ; +;;;;;;;;;;;;;;;;;;;; + +; Output buffering is a mechanism for controlling how much output data +; (excluding headers and cookies) PHP should keep internally before pushing that +; data to the client. If your application's output exceeds this setting, PHP +; will send that data in chunks of roughly the size you specify. +; Turning on this setting and managing its maximum buffer size can yield some +; interesting side-effects depending on your application and web server. +; You may be able to send headers and cookies after you've already sent output +; through print or echo. You also may see performance benefits if your server is +; emitting less packets due to buffered output versus PHP streaming the output +; as it gets it. On production servers, 4096 bytes is a good setting for performance +; reasons. +; Note: Output buffering can also be controlled via Output Buffering Control +; functions. +; Possible Values: +; On = Enabled and buffer is unlimited. (Use with caution) +; Off = Disabled +; Integer = Enables the buffer and sets its maximum size in bytes. +; Note: This directive is hardcoded to Off for the CLI SAPI +; Default Value: Off +; Development Value: 4096 +; Production Value: 4096 +; http://php.net/output-buffering +output_buffering = {{ getenv "PHP_OUTPUT_BUFFERING" "4096" }} + +; Duration of time, in seconds for which to cache realpath information for a given +; file or directory. For systems with rarely changing files, consider increasing this +; value. +; http://php.net/realpath-cache-ttl +realpath_cache_ttl = {{ getenv "PHP_REALPATH_CACHE_TTL" "120" }} + +; URL rewriter function rewrites URL on the fly by using +; output buffer. You can set target tags by this configuration. +; "form" tag is special tag. It will add hidden input tag to pass values. +; Refer to session.trans_sid_tags for usage. +; Default Value: "form=" +; Development Value: "form=" +; Production Value: "form=" +url_rewriter.tags = "{{ getenv "PHP_URL_REWRITER_TAGS" "a=href,area=href,frame=src,input=src,form=fakeentry" }}" + +;;;;;;;;;;;;;;;;; +; Miscellaneous ; +;;;;;;;;;;;;;;;;; + +; Decides whether PHP may expose the fact that it is installed on the server +; (e.g. by adding its signature to the Web server header). It is no security +; threat in any way, but it makes it possible to determine whether you use PHP +; on your server or not. +; http://php.net/expose-php +expose_php = {{ getenv "PHP_EXPOSE_PHP" "On" }} + +;;;;;;;;;;;;;;;;;;; +; Resource Limits ; +;;;;;;;;;;;;;;;;;;; + +; Maximum execution time of each script, in seconds +; http://php.net/max-execution-time +; Note: This directive is hardcoded to 0 for the CLI SAPI +max_execution_time = {{ getenv "PHP_MAX_EXECUTION_TIME" "120" }} + +; Maximum amount of time each script may spend parsing request data. It's a good +; idea to limit this time on productions servers in order to eliminate unexpectedly +; long running scripts. +; Note: This directive is hardcoded to -1 for the CLI SAPI +; Default Value: -1 (Unlimited) +; Development Value: 60 (60 seconds) +; Production Value: 60 (60 seconds) +; http://php.net/max-input-time +max_input_time = {{ getenv "PHP_MAX_INPUT_TIME" "60" }} + +; How many GET/POST/COOKIE input variables may be accepted +max_input_vars = {{ getenv "PHP_MAX_INPUT_VARS" "1000" }} + +; Maximum amount of memory a script may consume (512MB) +; http://php.net/memory-limit +memory_limit = {{ getenv "PHP_MEMORY_LIMIT" "512M" }} + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Error handling and logging ; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; This directive informs PHP of which errors, warnings and notices you would like +; it to take action for. The recommended way of setting values for this +; directive is through the use of the error level constants and bitwise +; operators. The error level constants are below here for convenience as well as +; some common settings and their meanings. +; By default, PHP is set to take action on all errors, notices and warnings EXCEPT +; those related to E_NOTICE and E_STRICT, which together cover best practices and +; recommended coding standards in PHP. For performance reasons, this is the +; recommend error reporting setting. Your production server shouldn't be wasting +; resources complaining about best practices and coding standards. That's what +; development servers and development settings are for. +; Note: The php.ini-development file has this setting as E_ALL. This +; means it pretty much reports everything which is exactly what you want during +; development and early testing. +; +; Error Level Constants: +; E_ALL - All errors and warnings (includes E_STRICT as of PHP 5.4.0) +; E_ERROR - fatal run-time errors +; E_RECOVERABLE_ERROR - almost fatal run-time errors +; E_WARNING - run-time warnings (non-fatal errors) +; E_PARSE - compile-time parse errors +; E_NOTICE - run-time notices (these are warnings which often result +; from a bug in your code, but it's possible that it was +; intentional (e.g., using an uninitialized variable and +; relying on the fact it is automatically initialized to an +; empty string) +; E_STRICT - run-time notices, enable to have PHP suggest changes +; to your code which will ensure the best interoperability +; and forward compatibility of your code +; E_CORE_ERROR - fatal errors that occur during PHP's initial startup +; E_CORE_WARNING - warnings (non-fatal errors) that occur during PHP's +; initial startup +; E_COMPILE_ERROR - fatal compile-time errors +; E_COMPILE_WARNING - compile-time warnings (non-fatal errors) +; E_USER_ERROR - user-generated error message +; E_USER_WARNING - user-generated warning message +; E_USER_NOTICE - user-generated notice message +; E_DEPRECATED - warn about code that will not work in future versions +; of PHP +; E_USER_DEPRECATED - user-generated deprecation warnings +; +; Common Values: +; E_ALL (Show all errors, warnings and notices including coding standards.) +; E_ALL & ~E_NOTICE (Show all errors, except for notices) +; E_ALL & ~E_NOTICE & ~E_STRICT (Show all errors, except for notices and coding standards warnings.) +; E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR (Show only errors) +; Default Value: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED +; Development Value: E_ALL +; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT +; http://php.net/error-reporting +error_reporting = {{ getenv "PHP_ERROR_REPORTING" "E_ALL & ~E_DEPRECATED & ~E_STRICT" }} + +; This directive controls whether or not and where PHP will output errors, +; notices and warnings too. Error output is very useful during development, but +; it could be very dangerous in production environments. Depending on the code +; which is triggering the error, sensitive information could potentially leak +; out of your application such as database usernames and passwords or worse. +; For production environments, we recommend logging errors rather than +; sending them to STDOUT. +; Possible Values: +; Off = Do not display any errors +; stderr = Display errors to STDERR (affects only CGI/CLI binaries!) +; On or stdout = Display errors to STDOUT +; Default Value: On +; Development Value: On +; Production Value: Off +; http://php.net/display-errors +display_errors = {{ getenv "PHP_DISPLAY_ERRORS" "Off" }} + +; The display of errors which occur during PHP's startup sequence are handled +; separately from display_errors. PHP's default behavior is to suppress those +; errors from clients. Turning the display of startup errors on can be useful in +; debugging configuration problems. We strongly recommend you +; set this to 'off' for production servers. +; Default Value: Off +; Development Value: On +; Production Value: Off +; http://php.net/display-startup-errors +display_startup_errors = {{ getenv "PHP_DISPLAY_STARTUP_ERRORS" "Off" }} + +; Besides displaying errors, PHP can also log errors to locations such as a +; server-specific log, STDERR, or a location specified by the error_log +; directive found below. While errors should not be displayed on productions +; servers they should still be monitored and logging is a great way to do that. +; Default Value: Off +; Development Value: On +; Production Value: On +; http://php.net/log-errors +log_errors = {{ getenv "PHP_LOG_ERRORS" "On" }} + +; Set maximum length of log_errors. In error_log information about the source is +; added. The default is 1024 and 0 allows to not apply any maximum length at all. +; http://php.net/log-errors-max-len +log_errors_max_len = {{ getenv "PHP_LOG_ERRORS_MAX_LEN" "1024" }} + +;;;;;;;;;;;;;;;;; +; Data Handling ; +;;;;;;;;;;;;;;;;; + +; Maximum size of POST data that PHP will accept. +; Its value may be 0 to disable the limit. It is ignored if POST data reading +; is disabled through enable_post_data_reading. +; http://php.net/post-max-size +post_max_size = {{ getenv "PHP_POST_MAX_SIZE" "32M" }} + +; Automatically add files before PHP document. +; http://php.net/auto-prepend-file +auto_prepend_file = {{ getenv "PHP_AUTO_PREPEND_FILE" }} + +; Automatically add files after PHP document. +; http://php.net/auto-append-file +auto_append_file = {{ getenv "PHP_AUTO_APPEND_FILE" }} + +;;;;;;;;;;;;;;;;;;;;;;;;; +; Paths and Directories ; +;;;;;;;;;;;;;;;;;;;;;;;;; + +; Determines the size of the realpath cache to be used by PHP. This value should +; be increased on systems where PHP opens many files to reflect the quantity of +; the file operations performed. +; Note: if open_basedir is set, the cache is disabled +; http://php.net/realpath-cache-size +realpath_cache_size = {{ getenv "PHP_REALPATH_CACHE_SIZE" "800K" }} + +;;;;;;;;;;;;;;;; +; File Uploads ; +;;;;;;;;;;;;;;;; + +; Maximum allowed size for uploaded files. +; http://php.net/upload-max-filesize +upload_max_filesize = {{ getenv "PHP_UPLOAD_MAX_FILESIZE" "100M" }} + +; Maximum number of files that can be uploaded via a single request +max_file_uploads = {{ getenv "PHP_MAX_FILE_UPLOADS" "20" }} + +;;;;;;;;;;;;;;;;;; +; Fopen wrappers ; +;;;;;;;;;;;;;;;;;; + +; Whether to allow the treatment of URLs (like http:// or ftp://) as files. +; http://php.net/allow-url-fopen +allow_url_fopen = {{ getenv "PHP_ALLOW_URL_FOPEN" "On" }} + +; Default timeout for socket based streams (seconds) +; http://php.net/default-socket-timeout +default_socket_timeout = {{ getenv "PHP_DEFAULT_SOCKET_TIMEOUT" "60" }} + +;;;;;;;;;;;;;;;;;;; +; Module Settings ; +;;;;;;;;;;;;;;;;;;; + +[Date] + +; Defines the default timezone used by the date functions +; http://php.net/date.timezone +date.timezone = {{ getenv "PHP_DATE_TIMEZONE" "America/Toronto"}} + +[Pdo_mysql] + +; If mysqlnd is used: Number of cache slots for the internal result set cache +; http://php.net/pdo_mysql.cache_size +pdo_mysql.cache_size = {{ getenv "PHP_PDO_MYSQL_CACHE_SIZE" "2000" }} + +[mail function] + +; For Unix only. You may supply arguments as well (default: "sendmail -t -i") +; or /usr/sbin/sendmail -t -i +; http://php.net/sendmail-path +sendmail_path = {{ getenv "PHP_SENDMAIL_PATH" "/bin/true" }} + +; Add X-PHP-Originating-Script: that will include uid of the script followed by the filename +mail.add_x_header = {{ getenv "PHP_MAIL_ADD_X_HEADER" "On" }} + +[ODBC] + +; Handling of binary data. 0 means passthru, 1 return as is, 2 convert to char. +; See the documentation on odbc_binmode and odbc_longreadlen for an explanation +; of odbc.defaultlrl and odbc.defaultbinmode +; http://php.net/odbc.defaultbinmode +odbc.defaultbinmode = 1 + +[MySQLi] + +; If mysqlnd is used: Number of cache slots for the internal result set cache +; http://php.net/mysqli.cache_size +mysqli.cache_size = {{ getenv "PHP_MYSQLI_CACHE_SIZE" "2000" }} + +[Session] + +; Handler used to store/retrieve data. +; http://php.net/session.save-handler +session.save_handler = {{ getenv "PHP_SESSION_SAVE_HANDLER" "files" }} +; Argument passed to save_handler. In the case of files, this is the path +; where data files are stored. Note: Windows users have to change this +; variable in order to use PHP's session functions. +; +; The path can be defined as: +; +; session.save_path = "N;/path" +; +; where N is an integer. Instead of storing all the session files in +; /path, what this will do is use subdirectories N-levels deep, and +; store the session data in those directories. This is useful if +; your OS has problems with many files in one directory, and is +; a more efficient layout for servers that handle many sessions. +; +; NOTE 1: PHP will not create this directory structure automatically. +; You can use the script in the ext/session dir for that purpose. +; NOTE 2: See the section on garbage collection below if you choose to +; use subdirectories for session storage +; +; The file storage module creates files using mode 600 by default. +; You can change that by using +; +; session.save_path = "N;MODE;/path" +; +; where MODE is the octal representation of the mode. Note that this +; does not overwrite the process's umask. +; http://php.net/session.save-path +session.save_path = "{{ getenv "PHP_SESSION_SAVE_PATH" "/tmp" }}" +; Whether to use cookies. +; http://php.net/session.use-cookies +session.use_cookies = {{ getenv "PHP_SESSION_USE_COOKIES" "1" }} +; This option forces PHP to fetch and use a cookie for storing and maintaining +; the session id. We encourage this operation as it's very helpful in combating +; session hijacking when not specifying and managing your own session id. It is +; not the be-all and end-all of session hijacking defense, but it's a good start. +; http://php.net/session.use-only-cookies +session.use_only_cookies = {{ getenv "PHP_SESSION_USE_ONLY_COOKIES" "1" }} +; Name of the session (used as cookie name). +; http://php.net/session.name +session.name = {{ getenv "PHP_SESSION_NAME" "PHPSESSID" }} +; Initialize session on request startup. +; http://php.net/session.auto-start +session.auto_start = {{ getenv "PHP_SESSION_AUTO_START" "0" }} + +; Lifetime in seconds of cookie or, if 0, until browser is restarted. +; http://php.net/session.cookie-lifetime +session.cookie_lifetime = {{ getenv "PHP_SESSION_COOKIE_LIFETIME" "2000" }} +; The path for which the cookie is valid. +; http://php.net/session.cookie-path +session.cookie_path = {{ getenv "PHP_SESSION_COOKIE_PATH" "/" }} +; The domain for which the cookie is valid. +; http://php.net/session.cookie-domain +session.cookie_domain = {{ getenv "PHP_SESSION_COOKIE_DOMAIN" }} +; Whether or not to add the httpOnly flag to the cookie, which makes it inaccessible to browser scripting languages such as JavaScript. +; http://php.net/session.cookie-httponly +session.cookie_httponly = {{ getenv "PHP_SESSION_COOKIE_HTTPONLY" }} + +; Handler used to serialize data. php is the standard serializer of PHP. +; http://php.net/session.serialize-handler +session.serialize_handler = {{ getenv "PHP_SESSION_SERIALIZE_HANDLER" "php" }} + +; Defines the probability that the 'garbage collection' process is started +; on every session initialization. The probability is calculated by using +; gc_probability/gc_divisor. Where session.gc_probability is the numerator +; and gc_divisor is the denominator in the equation. Setting this value to 1 +; when the session.gc_divisor value is 100 will give you approximately a 1% chance +; the gc will run on any give request. +; Default Value: 1 +; Development Value: 1 +; Production Value: 1 +; http://php.net/session.gc-probability +session.gc_probability = {{ getenv "PHP_SESSION_GC_PROBABILITY" "1" }} +; Defines the probability that the 'garbage collection' process is started on every +; session initialization. The probability is calculated by using the following equation: +; gc_probability/gc_divisor. Where session.gc_probability is the numerator and +; session.gc_divisor is the denominator in the equation. Setting this value to 1 +; when the session.gc_divisor value is 100 will give you approximately a 1% chance +; the gc will run on any give request. Increasing this value to 1000 will give you +; a 0.1% chance the gc will run on any give request. For high volume production servers, +; this is a more efficient approach. +; Default Value: 100 +; Development Value: 1000 +; Production Value: 1000 +; http://php.net/session.gc-divisor +session.gc_divisor = {{ getenv "PHP_SESSION_GC_DIVISOR" "100" }} +; After this number of seconds, stored data will be seen as 'garbage' and +; cleaned up by the garbage collection process. +; http://php.net/session.gc-maxlifetime +session.gc_maxlifetime = {{ getenv "PHP_SESSION_GC_MAXLIFETIME" "1440" }} + +; Check HTTP Referer to invalidate externally stored URLs containing ids. +; HTTP_REFERER has to contain this substring for the session to be +; considered as valid. +; http://php.net/session.referer-check +session.referer_check = {{ getenv "PHP_SESSION_REFERER_CHECK" "" }} + +; Set to {nocache,private,public,} to determine HTTP caching aspects +; or leave this empty to avoid sending anti-caching headers. +; http://php.net/session.cache-limiter +session.cache_limiter = {{ getenv "PHP_SESSION_CACHE_LIMITER" "nocache" }} +; Document expires after n minutes. +; http://php.net/session.cache-expire +session.cache_expire = {{ getenv "PHP_SESSION_CACHE_EXPIRE" "180" }} + +; trans sid support is disabled by default. +; Use of trans sid may risk your users' security. +; Use this option with caution. +; - User may send URL contains active session ID +; to other person via. email/irc/etc. +; - URL that contains active session ID may be stored +; in publicly accessible computer. +; - User may access your site with the same session ID +; always using URL stored in browser's history or bookmarks. +; http://php.net/session.use-trans-sid +session.use_trans_sid = {{ getenv "PHP_SESSION_USE_TRANS_SID" "0" }} + +; Set session ID character length. This value could be between 22 to 256. +; Shorter length than default is supported only for compatibility reason. +; Users should use 32 or more chars. +; http://php.net/session.sid-length +; Default Value: 32 +; Development Value: 26 +; Production Value: 26 +session.sid_length = {{ getenv "PHP_SESSION_SID_LENGTH" "32" }} + +; Define how many bits are stored in each character when converting +; the binary hash data to something readable. +; Possible values: +; 4 (4 bits: 0-9, a-f) +; 5 (5 bits: 0-9, a-v) +; 6 (6 bits: 0-9, a-z, A-Z, "-", ",") +; Default Value: 4 +; Development Value: 5 +; Production Value: 5 +; http://php.net/session.hash-bits-per-character +session.sid_bits_per_character = {{ getenv "PHP_SESSION_SID_BITS_PER_CHARACTER" "5" }} + +[Assertion] +zend.assertions = {{ getenv "PHP_ZEND_ASSERTIONS" "1" }} +assert.active = {{ getenv "PHP_ASSERT_ACTIVE" "On" }} diff --git a/config/php/templates/zz-www.conf.tmpl b/config/php/templates/zz-www.conf.tmpl new file mode 100644 index 000000000..1432222b3 --- /dev/null +++ b/config/php/templates/zz-www.conf.tmpl @@ -0,0 +1,70 @@ +[www] + +; Unix user/group of processes +; Note: The user is mandatory. If the group is not set, the default user's group +; will be used. +user = {{ getenv "PHP_FPM_USER" "www-data" }} +group = {{ getenv "PHP_FPM_GROUP" "www-data" }} + +listen = /var/run/php-fpm.sock +listen.owner = {{ getenv "PHP_FPM_USER" "www-data" }} +listen.group = {{ getenv "PHP_FPM_GROUP" "www-data" }} + +; Redirect worker stdout and stderr into main error log. If not set, stdout and +; stderr will be redirected to /dev/null according to FastCGI specs. +; Note: on highloaded environement, this can cause some delay in the page +; process time (several ms). +; Default Value: no +clear_env = {{ getenv "PHP_FPM_CLEAR_ENV" "yes" }} + +; Choose how the process manager will control the number of child processes. +; Possible Values: +; static - a fixed number (pm.max_children) of child processes; +; dynamic - the number of child processes are set dynamically based on the +; following directives. With this process management, there will be +; always at least 1 children. +; pm.max_children - the maximum number of children that can +; be alive at the same time. +; pm.start_servers - the number of children created on startup. +; pm.min_spare_servers - the minimum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is less than this +; number then some children will be created. +; pm.max_spare_servers - the maximum number of children in 'idle' +; state (waiting to process). If the number +; of 'idle' processes is greater than this +; number then some children will be killed. +; ondemand - no children are created at startup. Children will be forked when +; new requests will connect. The following parameter are used: +; pm.max_children - the maximum number of children that +; can be alive at the same time. +; pm.process_idle_timeout - The number of seconds after which +; an idle process will be killed. +; Note: This value is mandatory. +pm = {{ getenv "PHP_FPM_PM" "dynamic" }} +; The number of child processes to be created when pm is set to 'static' and the +; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. +; This value sets the limit on the number of simultaneous requests that will be +; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. +; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP +; CGI. The below defaults are based on a server without much resources. Don't +; forget to tweak pm.* to fit your needs. +; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' +; Note: This value is mandatory. Default docker is 5. +pm.max_children = {{ getenv "PHP_FPM_PM_MAX_CHILDREN" "20" }} +; The number of child processes created on startup. +; Note: Used only when pm is set to 'dynamic' +; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 +pm.start_servers = {{ getenv "PHP_FPM_PM_START_SERVERS" "5" }} +; The desired minimum number of idle server processes. +; Note: Used and Mandatory only when pm is set to 'dynamic' +pm.min_spare_servers = {{ getenv "PHP_FPM_PM_MIN_SPARE_SERVERS" "5" }} +; The desired maximum number of idle server processes. +; Note: Used only when pm is set to 'dynamic' +; Note: Mandatory when pm is set to 'dynamic' +pm.max_spare_servers = {{ getenv "PHP_FPM_PM_MAX_SPARE_SERVERS" "5" }} +; The number of requests each child process should execute before respawning. +; This can be useful to work around memory leaks in 3rd party libraries. For +; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. +; Default Value: 0 +pm.max_requests = {{ getenv "PHP_FPM_PM_MAX_REQUESTS" "500" }} diff --git a/config/supervisord/nginx_php/supervisord.conf b/config/supervisord/nginx_php/supervisord.conf new file mode 100644 index 000000000..2062b3792 --- /dev/null +++ b/config/supervisord/nginx_php/supervisord.conf @@ -0,0 +1,43 @@ +[supervisord] +user=%(ENV_APP_RUNNER_USER)s +nodaemon=true +logfile=/dev/null +logfile_maxbytes=0 +pidfile=/var/run/supervisord.pid +loglevel = INFO + +[unix_http_server] +file=/var/run/supervisor.sock +chmod=0700 +username=docker +password=docker + +[supervisorctl] +serverurl=unix:///var/run/supervisord.sock +username=docker +password=docker + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface + +[program:php-fpm] +command = /usr/local/sbin/php-fpm +autostart=true +autorestart=true +priority=5 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:nginx] +command=/usr/sbin/nginx -g "daemon off;" +autostart=true +autorestart=true +priority=10 +stdout_events_enabled=true +stderr_events_enabled=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 From d40d7446e5ac7ec10c3ab38c68e3fa8e1052f3a9 Mon Sep 17 00:00:00 2001 From: nikathone Date: Mon, 24 Feb 2020 13:58:36 -0500 Subject: [PATCH 044/107] Added bin folder which contains needed script for the app/drupal container. --- scripts/app_bin/docker-webserver-entrypoint | 53 +++++++++++++++++ scripts/app_bin/files_link | 43 ++++++++++++++ scripts/app_bin/install_drupal | 63 +++++++++++++++++++++ scripts/app_bin/wait_for_database | 37 ++++++++++++ 4 files changed, 196 insertions(+) create mode 100644 scripts/app_bin/docker-webserver-entrypoint create mode 100644 scripts/app_bin/files_link create mode 100644 scripts/app_bin/install_drupal create mode 100644 scripts/app_bin/wait_for_database diff --git a/scripts/app_bin/docker-webserver-entrypoint b/scripts/app_bin/docker-webserver-entrypoint new file mode 100644 index 000000000..3a5548109 --- /dev/null +++ b/scripts/app_bin/docker-webserver-entrypoint @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +set -e + +if [[ -n "${DEBUG}" ]]; then + set -x +fi + +# usage: file_env VAR [DEFAULT] +# ie: file_env 'DB_PASSWORD' 'example' +# (will allow for "$DB_PASSWORD_FILE" to fill in the value of +# "$DB_PASSWORD" from a file, especially for Docker's secrets feature) +file_env() { + local var="$1" + local fileVar="${var}_FILE" + local def="${2:-}" + if [[ "${!var:-}" ]] && [[ "${!fileVar:-}" ]]; then + echo >&2 "error: both $var and $fileVar are set (but are exclusive)" + exit 1 + fi + local val="${def}" + if [[ "${!var:-}" ]]; then + val="${!var}" + elif [[ "${!fileVar:-}" ]]; then + val="$(< "${!fileVar}")" + fi + export "$var"="$val" + unset "$fileVar" +} + +if [[ -f "${APP_DOCROOT}/sites/default/settings.${APP_NAME}.php" ]]; then + file_env "APP_NAME" + file_env "APP_ACCOUNT_PASS" + file_env "DB_NAME" + file_env "DB_USER" + file_env "DB_PASSWORD" +fi + +# Make sure files folder is linked at the proper folder +/usr/local/bin/files_link "${APP_DOCROOT}/sites/default/files" + +# set custom config files for the drupal .env where the credentials are. +/usr/local/bin/confd -onetime -backend env + +# Delay container to start until the database server is ready. +/usr/local/bin/wait_for_database + +# Attempt to install drupal. +if [[ "${AUTO_INSTALL}" == "true" ]]; then + /usr/local/bin/install_drupal +fi + +exec "$@" diff --git a/scripts/app_bin/files_link b/scripts/app_bin/files_link new file mode 100644 index 000000000..bbd10f417 --- /dev/null +++ b/scripts/app_bin/files_link @@ -0,0 +1,43 @@ +#!/usr/bin/env bash + +set -e + +if [[ -n "${DEBUG}" ]]; then + set -x +fi + +app_public_dir=$1 + +# Add symlink from persistent files volume to application's storage public dir. +if [[ -n "${app_public_dir}" ]]; then + echo >&2 + echo >&2 "[INFO] Application's public storage directory specified, trying to symlink from files persistent volume" + echo >&2 + + if [[ -d "${app_public_dir}" ]]; then + if [[ ! -L "${app_public_dir}" ]]; then + if [[ "$(ls -A "${app_public_dir}")" ]]; then + echo >&2 + echo >&2 "[ERROR] Failed to symlink public storage directory to a persistent volume" + echo >&2 " Directory ${app_public_dir} must not exists or be empty" + echo >&2 " (use files import to migrate existing public files)" + echo >&2 + exit 1 + # If dir is not symlink and empty, remove it and link. + else + echo >&2 + echo >&2 "[INFO] Empty public storage dir detected: removing and symlinking '${app_public_dir}' -> ''${FILES_DIR}/public'" + echo >&2 + rm -rf "${app_public_dir}" + ln -sf "${FILES_DIR}/public" "${app_public_dir}" + fi + else + echo "Symlink already in place" + fi + else + echo >&2 + echo >&2 "[INFO] No public storage dir detected: just symlinking '${app_public_dir}' -> '${FILES_DIR}/public'" + echo >&2 + ln -sf "${FILES_DIR}/public" "${app_public_dir}" + fi +fi diff --git a/scripts/app_bin/install_drupal b/scripts/app_bin/install_drupal new file mode 100644 index 000000000..7de9198cf --- /dev/null +++ b/scripts/app_bin/install_drupal @@ -0,0 +1,63 @@ +#!/usr/bin/env bash + +set -e + +if [[ -n "${DEBUG}" ]]; then + set -x +fi + +if [[ "${APP_ENV}" == "dev" && ! -f "${APP_DOCROOT}/index.php" ]]; then + cd ${APP_ROOT} + composer install --classmap-authoritative +fi; + +# Ensure we are in the app web. +cd ${APP_DOCROOT} + +# Determine if site already installed +site_installed=$(${APP_ROOT}/vendor/bin/drush status bootstrap | grep -q Successful; echo "$?") + +if [[ -n "${site_installed}" && "${site_installed}" -eq 0 ]]; then + echo >&2 + echo >&2 '[INFO] Site already installed' + echo >&2 ' You might need to run ../vendor/bin/drush config-import if' + echo >&2 ' the database configurations are out of sync.' + echo >&2 +else + site_install_options=( + --yes + --verbose + --account-name=${APP_ACCOUNT_NAME} + --account-mail=${APP_ACCOUNT_MAIL} + --account-pass=${APP_ACCOUNT_PASS} + ) + if [[ "$(ls -A ${APP_ROOT}/config/sync)" && -f ${APP_ROOT}/config/sync/core.extension.yml ]]; then + echo >&2 + echo >&2 '[INFO] Installing site using existing configurations' + echo >&2 '[INFO] Switching to minimal profile to allow existing config to install.' + echo >&2 ' See https://www.drupal.org/node/2897299 for more information.' + echo >&2 + sed -i 's/standard:/minimal:/g' ${APP_ROOT}/config/sync/core.extension.yml + sed -i 's/profile: standard/profile: minimal/g' ${APP_ROOT}/config/sync/core.extension.yml + site_install_options+=( + --existing-config + minimal + ) + else + echo >&2 + echo >&2 '[INFO] Installing site from scratch' + echo >&2 + site_install_options+=( + --site-name=${APP_NAME} + --site-mail=${APP_ACCOUNT_MAIL} + standard + ) + fi + + if [[ -n "${DEBUG}" ]]; then + site_install_options+=( --debug ) + fi + + # Installing drupal + ${APP_ROOT}/vendor/bin/drush site:install "${site_install_options[@]}" +fi diff --git a/scripts/app_bin/wait_for_database b/scripts/app_bin/wait_for_database new file mode 100644 index 000000000..7f2da2cc2 --- /dev/null +++ b/scripts/app_bin/wait_for_database @@ -0,0 +1,37 @@ +#!/usr/bin/env bash + +set -e + +if [[ -n "${DEBUG}" ]]; then + set -x +fi + +function fail { + echo $1 >&2 + exit 1 +} + +function retry { + local n=1 + local max=5 + local delay=5 + while true; do + "$@" && break || { + if [[ $n -lt $max ]]; then + ((n++)) + echo "Command failed. Attempt $n/$max:" + sleep $delay; + else + fail "The command has failed after $n attempts." + fi + } + done +} + +# Check connection to the database and verify if the database is created +retry mysql --silent --quick --force \ + --host=${DB_HOST} \ + --port=${DB_PORT} \ + --user=${DB_USER} \ + --password=${DB_PASSWORD} \ + --execute "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='${DB_NAME}'" >/dev/null 2>&1 From c48f91ca78280bd6f3f69a4b0bd8f7609481da83 Mon Sep 17 00:00:00 2001 From: nikathone Date: Tue, 25 Feb 2020 12:46:35 -0500 Subject: [PATCH 045/107] Added app/drupal dockerfile and created the base webserver dockerfile. --- drupal.Dockerfile | 191 ++++++++++++++++++++++++++++++ images/nginx-php/Dockerfile | 168 ++++++++++++++++++++++++++ images/nginx-php/supervisord.conf | 43 +++++++ 3 files changed, 402 insertions(+) create mode 100644 drupal.Dockerfile create mode 100644 images/nginx-php/Dockerfile create mode 100644 images/nginx-php/supervisord.conf diff --git a/drupal.Dockerfile b/drupal.Dockerfile new file mode 100644 index 000000000..bbab7e8db --- /dev/null +++ b/drupal.Dockerfile @@ -0,0 +1,191 @@ +ARG build_environment=prod +ARG code_dir=./code +ARG base_image_tag=latest +ARG composer_version=1.9.3 +ARG templates_dir=./config +ARG auto_install=false + +# +# Stage 1: PHP Dependencies +# +# @TODO handle scafolded files +FROM composer:${composer_version} as composer-build +ARG code_dir +ARG build_environment +ENV COMPOSER_INSTALL_FLAGS \ + --ansi \ + --no-suggest \ + --prefer-dist \ + --no-interaction \ + --ignore-platform-reqs + +ENV DRUPAL_COMPOSER_DIRECTORIES \ + web/core \ + web/modules/contrib \ + web/profiles/contrib \ + web/themes/contrib \ + web/libraries \ + drush/contrib + +WORKDIR /root/.ssh +RUN chmod 0600 /root/.ssh \ + && ssh-keyscan -t rsa bitbucket.org >> known_hosts \ + && ssh-keyscan -t rsa github.com >> known_hosts \ + # To speed up download. + && composer global require hirak/prestissimo "${COMPOSER_INSTALL_FLAGS}" + +WORKDIR /app + +COPY ${code_dir}/composer.json ${code_dir}/composer.lock ./ + +RUN set -eux; \ + flags="${COMPOSER_INSTALL_FLAGS}"; \ + if [ "$build_environment" == "prod" ]; then \ + flags="${COMPOSER_INSTALL_FLAGS} --no-dev"; \ + fi; \ + composer install $flags \ + # make dummy directory just in case no drupal contrib related dependencies was created. + && for dir in $DRUPAL_COMPOSER_DIRECTORIES; do \ + if [ ! -d $dir ]; then \ + mkdir -p $dir; \ + fi; \ + done; + +# +# Stage 2: Any node related dependencies can be build here. e.g. compile scss to css +# +# Example coming soon + +# +# Stage 3: The base app/drupal +# +FROM registry.gitlab.com/nikathone/drupal-docker-good-defaults/php-nginx:${base_image_tag} as base +ARG code_dir +ARG auto_intall +ARG templates_dir +ARG app_runner_user=drupal +ARG app_runner_user_id=1000 +ARG app_runner_group=drupal +ARG app_runner_group_id=1000 +ARG app_bin_dir=./scripts/app_bin + +ENV PATH=${PATH}:${APP_ROOT}/vendor/bin \ + PHP_EXPOSE_PHP=Off \ + PHP_FPM_USER=${app_runner_user} \ + PHP_FPM_GROUP=${app_runner_group} \ + NGINX_LISTEN_PORT=8080 \ + DEFAULT_USER=${app_runner_user} \ + APP_NAME=drupal \ + AUTO_INSTALL=${auto_install:-false} \ + APP_RUNNER_USER=${app_runner_user} \ + APP_RUNNER_USER_ID=${app_runner_user_id:-1000} \ + APP_RUNNER_GROUP=${app_runner_group} \ + APP_RUNNER_GROUP_ID=${app_runner_group_id:-1000} + +# Copy custom excutable scripts +COPY ${app_bin_dir} /usr/local/bin/ + +# Copy custom configuration files for PHP and NGINX +COPY ${templates_dir}/php/conf.d /etc/confd/conf.d +COPY ${templates_dir}/nginx/conf.d /etc/confd/conf.d +COPY ${templates_dir}/php/templates /etc/confd/templates +COPY ${templates_dir}/nginx/templates /etc/confd/templates + +# Make sure docker-webserver-entrypoint and other scripts are executable +RUN chmod -R +x /usr/local/bin/; \ + # apply custom configurations based on confd templates + /usr/local/bin/confd -onetime -backend env \ + # clean the content of confd so that the app can add it's templates later in the process + && rm -rf /etc/confd/* + +# Add and configure app runner user +RUN set -xe; \ + # Delete existing user/group if uid/gid occupied. + existing_group=$(getent group "${APP_RUNNER_GROUP_ID}" | cut -d: -f1); \ + if [ -n "${existing_group}" ]; then delgroup "${existing_group}"; fi; \ + existing_user=$(getent passwd "${APP_RUNNER_USER_ID}" | cut -d: -f1); \ + if [ -n "${existing_user}" ]; then deluser "${existing_user}"; fi; \ + \ + # Ensure app runner user/group exists + addgroup --system --gid ${APP_RUNNER_GROUP_ID} ${APP_RUNNER_GROUP}; \ + adduser --system --disabled-password --ingroup ${APP_RUNNER_GROUP} --shell /bin/bash --uid ${APP_RUNNER_USER_ID} ${APP_RUNNER_USER}; \ + usermod --append --groups ${NGINX_USER_GROUP} ${APP_RUNNER_USER} \ + # Other app runner user related configurations. See bin/config_app_runner_user + && config_app_runner_user \ + \ + # Make sure that files dir have proper permissions. + && mkdir -p ${FILES_DIR}/public; \ + mkdir -p ${FILES_DIR}/private; \ + # Ensure the files dir is owned by nginx user + chown -R ${NGINX_USER}:${NGINX_USER_GROUP} ${FILES_DIR} + +EXPOSE $NGINX_LISTEN_PORT +ENTRYPOINT [ "docker-webserver-entrypoint" ] +CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/conf.d/supervisord.conf"] + +WORKDIR ${APP_ROOT} + +# Copy entire code files. The .dockerignore file ensures that ignored paths are +# not copied from the host machine into the image. +COPY --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} ${code_dir} ./ +# Copy code and composer generated files and directories +# @TODO Copy scalfolded files. +COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/vendor ./vendor +COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/web/core ./web/core +COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/web/modules/contrib ./web/modules/contrib +COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/web/themes/contrib ./web/themes/contrib +COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/web/profiles/contrib ./web/profiles/contrib +COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/web/libraries ./web/libraries +COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/drush/contrib ./drush/contrib +# If stage 2 available genrated front end css and css artifacts files can be copied + +# Copy custom configuration files for the app/drupal. We run confd in the docker entrypoint for this. +COPY ${templates_dir}/drupal/confd /etc/confd + +# +# Stage 4: The production setup +# +FROM base AS prod +ENV APP_ENV=prod + +# Using the production php.ini +RUN mv ${PHP_INI_DIR}/php.ini-production ${PHP_INI_DIR}/php.ini + +USER ${APP_RUNNER_USER} + +# +# Stage 5: The dev setup +# +FROM base AS dev +ARG composer_version +ARG templates_dir +ARG PHP_XDEBUG +ARG PHP_XDEBUG_DEFAULT_ENABLE +ARG PHP_XDEBUG_REMOTE_CONNECT_BACK +ARG PHP_XDEBUG_REMOTE_HOST +ARG PHP_IDE_CONFIG + +ENV APP_ENV=dev \ + DEBUG=true + +# Install development tools. +RUN pecl install xdebug-2.7.1; \ + docker-php-ext-enable xdebug; \ + # Adding the dev php.ini + mv ${PHP_INI_DIR}/php.ini-development ${PHP_INI_DIR}/php.ini + +COPY ${templates_dir}/php/conf.d/docker-php-ext-xdebug.ini.toml /etc/confd/conf.d/docker-php-ext-xdebug.ini.toml +COPY ${templates_dir}/php/templates/docker-php-ext-xdebug.ini.tmpl /etc/confd/templates/docker-php-ext-xdebug.ini.tmpl + +# Apply xdebug configurations +RUN /usr/local/bin/confd -onetime -backend env \ + # Delete xdebug configuration template files + && rm /etc/confd/conf.d/docker-php-ext-xdebug.ini.toml /etc/confd/templates/docker-php-ext-xdebug.ini.tmpl + +# Copy composer binary from official Composer image. Notice we didn't need composer for prod stage. +# @TODO try to use composer_version here +COPY --from=composer:1.9.3 /usr/bin/composer /usr/bin/composer + +EXPOSE 9000 + +USER ${APP_RUNNER_USER} diff --git a/images/nginx-php/Dockerfile b/images/nginx-php/Dockerfile new file mode 100644 index 000000000..f473d958d --- /dev/null +++ b/images/nginx-php/Dockerfile @@ -0,0 +1,168 @@ +ARG php_version=7.2.27 +ARG php_pkg_release=fpm-buster + +FROM php:${php_version}-${php_pkg_release} + +ARG nginx_version=1.17.8 +ARG nginx_njs_version=0.3.8 +ARG nginx_pkg_release=1~buster + +ENV CONFD_VERSION=0.16.0 \ + CONFD_SHA256SUM="255d2559f3824dd64df059bdc533fd6b697c070db603c76aaf8d1d5e6b0cc334" \ + FILES_DIR="/mnt/files" \ + APP_ROOT="/var/www/app" \ + APP_DOCROOT="/var/www/app/web" \ + APP_RUNNER_USER=root + +# Install apt dependencies +# @TODO use specific versions of some of these to avoid version conflict or breaking +RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \ + # utilitities cmd + dos2unix rsync wget findutils \ + # for imap + libc-client2007e-dev libkrb5-dev \ + # for ssh client only + openssh-client \ + # for git + git \ + # for bz2 + bzip2 libbz2-dev \ + # for gd + # libfreetype6 libfreetype6-dev libpng-tools libjpeg-dev libgmp-dev libwebp-dev \ + libfreetype6 libfreetype6-dev libpng-tools libgmp-dev libwebp-dev \ + # For image optimization + jpegoptim optipng pngquant \ + # For imagick + imagemagick libmagickwand-dev \ + # For nginx cgi-fcgi + libfcgi0ldbl \ + # for intl + libicu-dev \ + # for mcrypt + libmcrypt-dev \ + # for ldap + libldap2-dev \ + # for zip + libzip-dev zip unzip \ + # for xslt + libxslt1-dev \ + # for postgres + libpq-dev \ + # for tidy + libtidy-dev \ + # for yaml + libyaml-dev \ + # for command like drush sqlc/sqlq which need a mysql client + mariadb-client \ + # for supervsisor (http://supervisord.org/) + supervisor; \ + # install confd + wget -O /usr/local/bin/confd "https://github.com/kelseyhightower/confd/releases/download/v${CONFD_VERSION}/confd-${CONFD_VERSION}-linux-amd64"; \ + chmod +x /usr/local/bin/confd; \ + # verify confd signature + sha256sum /usr/local/bin/confd | grep -q "${CONFD_SHA256SUM}"; \ + exit $?; \ + rm -r /var/lib/apt/lists/* + +# Install and enable php extensions using the helper script provided by the base +# image +RUN docker-php-ext-configure imap --with-kerberos --with-imap-ssl \ + && docker-php-ext-configure gd --with-gd --with-webp-dir \ + --with-freetype-dir=/usr/include/freetype2 \ + --with-jpeg-dir=/usr/include/ \ + --with-png-dir=/usr/include/ \ + && docker-php-ext-configure intl --enable-intl \ + && docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu \ + && docker-php-ext-configure zip --with-libzip \ + && docker-php-ext-install -j$(nproc) \ + bcmath \ + bz2 \ + calendar \ + exif \ + gd \ + gettext \ + gmp \ + imap \ + intl \ + ldap \ + mysqli \ + opcache \ + pcntl \ + pdo_mysql \ + pdo_pgsql \ + pgsql \ + soap \ + sockets \ + tidy \ + xmlrpc \ + xsl \ + zip \ + # pecl related extensions + && pecl install \ + apcu-5.1.17 \ + imagick-3.4.4 \ + mcrypt-1.0.1 \ + yaml-2.0.4 \ + && docker-php-ext-enable \ + apcu \ + imagick \ + mcrypt \ + yaml + +# install nginx (copied from official nginx Dockerfile https://github.com/nginxinc/docker-nginx/blob/c817e28dd68b6daa33265a8cb527b1c4cd723b59/mainline/buster/Dockerfile) +ENV NGINX_VERSION=${nginx_version} \ + NGINX_PKG_RELEASE=${nginx_pkg_release} \ + NJS_VERSION=${nginx_njs_version} \ + NGINX_USER=www-data \ + NGINX_USER_GROUP=www-data + +RUN set -x \ + # Ensure nginx user/group first, to be consistent throughout docker variants + && user_exists=$(id -u ${NGINX_USER} > /dev/null 2>&1; echo $?) \ + && if [ user_exists -eq 0 ]; then \ + addgroup --system --gid 101 ${NGINX_USER_GROUP}; \ + adduser --system --disabled-login \ + --ingroup ${NGINX_USER_GROUP} --no-create-home \ + --home /nonexistent --gecos "${NGINX_USER} user" \ + --shell /bin/false --uid 101 ${NGINX_USER}; \ + fi \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 \ + && \ + NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \ + found=''; \ + for server in \ + ha.pool.sks-keyservers.net \ + hkp://keyserver.ubuntu.com:80 \ + hkp://p80.pool.sks-keyservers.net:80 \ + pgp.mit.edu \ + ; do \ + echo "Fetching GPG key $NGINX_GPGKEY from $server"; \ + apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \ + done; \ + test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \ + apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \ + && nginxPackages=" \ + nginx=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ + nginx-module-xslt=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ + nginx-module-geoip=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ + nginx-module-image-filter=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ + nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-${NGINX_PKG_RELEASE} \ + " \ + # Arches officialy built by upstream + && echo "deb https://nginx.org/packages/mainline/debian/ buster nginx" >> /etc/apt/sources.list.d/nginx.list \ + && apt-get update \ + && apt-get install --no-install-recommends --no-install-suggests -y \ + $nginxPackages \ + gettext-base \ + && apt-get remove --purge --auto-remove -y \ + && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \ + # Create default place to store files generated by the app + && install -o ${NGINX_USER} -g ${NGINX_USER} -d "${FILES_DIR}/public" "${FILES_DIR}/private"; \ + chmod -R 775 "${FILES_DIR}" + +EXPOSE 80 443 + +# Copy custom supervisord configuration file +COPY ./supervisord.conf /etc/supervisor/conf.d/supervisord.conf +CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/conf.d/supervisord.conf"] diff --git a/images/nginx-php/supervisord.conf b/images/nginx-php/supervisord.conf new file mode 100644 index 000000000..2062b3792 --- /dev/null +++ b/images/nginx-php/supervisord.conf @@ -0,0 +1,43 @@ +[supervisord] +user=%(ENV_APP_RUNNER_USER)s +nodaemon=true +logfile=/dev/null +logfile_maxbytes=0 +pidfile=/var/run/supervisord.pid +loglevel = INFO + +[unix_http_server] +file=/var/run/supervisor.sock +chmod=0700 +username=docker +password=docker + +[supervisorctl] +serverurl=unix:///var/run/supervisord.sock +username=docker +password=docker + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface + +[program:php-fpm] +command = /usr/local/sbin/php-fpm +autostart=true +autorestart=true +priority=5 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:nginx] +command=/usr/sbin/nginx -g "daemon off;" +autostart=true +autorestart=true +priority=10 +stdout_events_enabled=true +stderr_events_enabled=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 From c077ba98f262f9eb6abb91c68d6fdc15ddaaaa14 Mon Sep 17 00:00:00 2001 From: nikathone Date: Tue, 25 Feb 2020 12:47:23 -0500 Subject: [PATCH 046/107] Moved supervisord config under nginx and php image. --- config/supervisord/nginx_php/supervisord.conf | 43 ------------------- 1 file changed, 43 deletions(-) delete mode 100644 config/supervisord/nginx_php/supervisord.conf diff --git a/config/supervisord/nginx_php/supervisord.conf b/config/supervisord/nginx_php/supervisord.conf deleted file mode 100644 index 2062b3792..000000000 --- a/config/supervisord/nginx_php/supervisord.conf +++ /dev/null @@ -1,43 +0,0 @@ -[supervisord] -user=%(ENV_APP_RUNNER_USER)s -nodaemon=true -logfile=/dev/null -logfile_maxbytes=0 -pidfile=/var/run/supervisord.pid -loglevel = INFO - -[unix_http_server] -file=/var/run/supervisor.sock -chmod=0700 -username=docker -password=docker - -[supervisorctl] -serverurl=unix:///var/run/supervisord.sock -username=docker -password=docker - -[rpcinterface:supervisor] -supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface - -[program:php-fpm] -command = /usr/local/sbin/php-fpm -autostart=true -autorestart=true -priority=5 -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 - -[program:nginx] -command=/usr/sbin/nginx -g "daemon off;" -autostart=true -autorestart=true -priority=10 -stdout_events_enabled=true -stderr_events_enabled=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 From d37a0191aa54f426b545a513d41dd2eb0259ec70 Mon Sep 17 00:00:00 2001 From: nikathone Date: Tue, 25 Feb 2020 15:21:49 -0500 Subject: [PATCH 047/107] Made a first pass for the default drupal dockerfile and docker-compose file. --- .gitignore | 4 + docker-compose.yml | 193 +++++++++++++++++++++------------------------ drupal.Dockerfile | 6 +- 3 files changed, 100 insertions(+), 103 deletions(-) diff --git a/.gitignore b/.gitignore index 099ab0cc3..87a31dd38 100644 --- a/.gitignore +++ b/.gitignore @@ -68,6 +68,10 @@ site .vscode config/proxy/acme.json +#=================== +# Drupal and Mariadb files persistant storage +*_files + #=================== ## Wodby proto specific codebase diff --git a/docker-compose.yml b/docker-compose.yml index fbf467d89..dd0f620ea 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '3.7' +version: '2.4' ## ISLE 8 Draft Prototype ## Feb, 2020 @@ -6,104 +6,91 @@ version: '3.7' ## MVP 2 - Microservices & Connectors (Houdini, Homarus, Hypercube, FITS, ActiveMQ and Cantaloupe) services: - - apache: - image: wodby/apache:$APACHE_TAG - container_name: "${PROJECT_NAME}_apache" - networks: - - internal - depends_on: - - php - environment: - APACHE_LOG_LEVEL: debug - APACHE_BACKEND_HOST: php - APACHE_VHOST_PRESET: php - APACHE_DOCUMENT_ROOT: /var/www/html/web - volumes: - - ./codebase:/var/www/html - - ./jwt:/opt/jwt -## For macOS users (https://wodby.com/docs/stacks/drupal/local#docker-for-mac) -## - ./codebase:/var/www/html:cached # User-guided caching -## - docker-sync:/var/www/html # Docker-sync - labels: - - "traefik.http.routers.${PROJECT_NAME}_apache.rule=Host(`${PROJECT_BASE_URL}`)" - - mariadb: - ## Using for protoype database - image: wodby/mariadb:$MARIADB_TAG - container_name: "${PROJECT_NAME}_mariadb" - networks: - - internal - stop_grace_period: 30s + image: mariadb:10.3.22 environment: - MYSQL_ROOT_PASSWORD: $DB_ROOT_PASSWORD - MYSQL_DATABASE: $DB_NAME - MYSQL_USER: $DB_USER - MYSQL_PASSWORD: $DB_PASSWORD + MYSQL_DATABASE: ${DB_NAME:-drupal} + MYSQL_USER: ${DB_USER:-drupal} + MYSQL_PASSWORD: ${DB_PASSWORD:-dbpassword} + MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-dbpassword} volumes: - ## TO DO: How to allow for slow query log setup? - ## TO DO: Customization: Allow for additional tuning of MySQL. - mariadb-data:/var/lib/mysql - # - ./mariadb-init:/docker-entrypoint-initdb.d # Place init .sql file(s) here. if needed e.g. fedora. - + healthcheck: + # https://dev.mysql.com/doc/refman/8.0/en/mysqladmin.html#command_mysqladmin_ping + test: ["CMD", "mysqladmin", "ping", "--silent"] - php: - image: wodby/drupal-php:$PHP_TAG - container_name: "${PROJECT_NAME}_php" - networks: - - internal + drupal: + build: + context: ./ + dockerfile: ./drupal.Dockerfile + target: dev + args: + - code_dir=./codebase + - auto_install=true + - templates_dir=./config + - app_bin_dir=./scripts/app_bin + - base_image_tag=latest + - build_environment=dev + # XDEBUG confd build time args - you can set it to 0 turn it off. + - PHP_XDEBUG=${PHP_XDEBUG:-1} + - PHP_IDE_CONFIG="serverName=${PHP_IDE_CONFIG_SERVER_NAME:-islandora.localhost}" + - PHP_XDEBUG_REMOTE_HOST=${PHP_XDEBUG_REMOTE_HOST:-host.docker.internal} + #-PHP_XDEBUG_REMOTE_HOST: host.docker.internal # Docker 18.03+ Mac/Win + #-PHP_XDEBUG_REMOTE_HOST: 172.17.0.1 # Linux + #-PHP_XDEBUG_REMOTE_HOST: 10.254.254.254 # macOS, Docker < 18.03 + #-PHP_XDEBUG_REMOTE_HOST: 10.0.75.1 # Windows, Docker < 18.03 + - PHP_XDEBUG_DEFAULT_ENABLE=${PHP_XDEBUG_DEFAULT_ENABLE:-1} + - PHP_XDEBUG_REMOTE_CONNECT_BACK=${PHP_XDEBUG_REMOTE_CONNECT_BACK:-0} + # NGINX confd build time args + - NGINX_SERVER_ROOT=${APP_DOCROOT:-/var/www/app/web} + - NGINX_LISTEN_PORT=${NGINX_LISTEN_PORT:-8080} + depends_on: + mariadb: + condition: service_healthy env_file: - php.env environment: - # PHP_SENDMAIL_PATH: /usr/sbin/sendmail -t -i -S mailhog:1025 - # PHP_SENDMAIL_PATH: /usr/sbin/sendmail -t -i -S opensmtpd:25 - DB_HOST: $DB_HOST - DB_PORT: $DB_PORT - DB_USER: $DB_USER - DB_PASSWORD: $DB_PASSWORD - DB_NAME: $DB_NAME - DB_DRIVER: $DB_DRIVER - PHP_FPM_USER: wodby - PHP_FPM_GROUP: wodby - COLUMNS: 80 # Set 80 columns for docker exec -it. -## Read instructions at https://wodby.com/docs/stacks/php/local/#xdebug -# PHP_XDEBUG: 1 -# PHP_XDEBUG_DEFAULT_ENABLE: 1 -# PHP_XDEBUG_REMOTE_CONNECT_BACK: 0 -# PHP_IDE_CONFIG: serverName=my-ide -# PHP_XDEBUG_IDEKEY: "my-ide" -# PHP_XDEBUG_REMOTE_HOST: host.docker.internal # Docker 18.03+ Mac/Win -# PHP_XDEBUG_REMOTE_HOST: 172.17.0.1 # Linux -# PHP_XDEBUG_REMOTE_HOST: 10.254.254.254 # macOS, Docker < 18.03 -# PHP_XDEBUG_REMOTE_HOST: 10.0.75.1 # Windows, Docker < 18.03 -# PHP_XDEBUG_REMOTE_LOG: /tmp/php-xdebug.log - volumes: - - ./scripts:/scripts - - ./codebase:/var/www/html - - ./config/drupal:/opt/drupal_config - - ./jwt:/opt/jwt -## For macOS users (https://wodby.com/docs/stacks/drupal/local#docker-for-mac) -# - ./codebase:/var/www/html:cached # User-guided caching -# - docker-sync:/var/www/html # Docker-sync -## For XHProf and Xdebug profiler traces -# - files:/mnt/files - - - solr: - image: wodby/solr:$SOLR_TAG - container_name: "${PROJECT_NAME}_solr" - networks: - - internal - environment: - SOLR_DEFAULT_CONFIG_SET: $SOLR_CONFIG_SET - SOLR_HEAP: 1024m + APP_NAME: ${APP_NAME:-islandora} + APP_ROOT: ${APP_ROOT:-/var/www/app} + APP_ACCOUNT_NAME: ${DRUPAL_USER_NAME:-islandora} + APP_ACCOUNT_MAIL: ${DRUPAL_USER_EMAIL:-islandora@example.com} + DB_NAME: ${DB_NAME:-drupal} + DB_USER: ${DB_USER:-drupal} + DB_PASSWORD: ${DB_PASSWORD:-dbpassword} + DB_HOST: "mariadb" + DB_PORT: ${DB_PORT:-3306} + DB_DRIVER: ${DB_DRIVER:-mysql} + DRUSH_OPTIONS_URI: ${DRUSH_OPTIONS_URI} + NGINX_LISTEN_PORT: 8080 volumes: - - solr-data:/opt/solr/server/solr - # TO DO: Determine what type of container handling is needed, exposed ports etc? + # set delegated mode here on docker for mac for faster disk I/O + - ./codebase:${APP_ROOT:-/var/www/app}:delegated + - ./drupal_files:${FILES_DIR:-/mnt/files} + # Anonymous volumes as a workaround to prevent empty or non existent host + # folders from accidentally getting mounted in container and overwrite the + # composer build stage files. + - ${APP_ROOT:-/var/www/app}/vendor + - ${APP_DOCROOT:-/var/www/app/web}/modules/contrib + - ${APP_DOCROOT:-/var/www/app/web}/themes/contrib + - ${APP_DOCROOT:-/var/www/app/web}/profiles/contrib + - ${APP_DOCROOT:-/var/www/app/web}/libraries labels: - - "traefik.http.routers.${PROJECT_NAME}_solr.rule=Host(`solr.${PROJECT_BASE_URL}`)" + - "traefik.http.routers.${APP_NAME:-islandora}.rule=Host(`${PROJECT_BASE_URL:-islandora.localhost}`)" + - "traefik.http.services.${APP_NAME:-islandora}.loadbalancer.server.port=8080" + # solr: + # image: wodby/solr:$SOLR_TAG + # container_name: "${PROJECT_NAME}_solr" + # networks: + # - internal + # environment: + # SOLR_DEFAULT_CONFIG_SET: $SOLR_CONFIG_SET + # SOLR_HEAP: 1024m + # volumes: + # - solr-data:/opt/solr/server/solr + # # TO DO: Determine what type of container handling is needed + # labels: + # - "traefik.http.routers.${PROJECT_NAME}_solr.rule=Host(`solr.${PROJECT_BASE_URL}`)" cantaloupe: image: lyrasis/cantaloupe:latest # should this be 4.0-1 instead? @@ -141,7 +128,7 @@ services: - ./jwt:/opt/jwt - ./config/crayfish/php.ini:/usr/local/etc/php/php.ini #labels: - # - "traefik.http.routers.${PROJECT_NAME}_houdini.rule=Host(`houdini.${PROJECT_BASE_URL}`)" + # - "traefik.http.routers.${PROJECT_NAME}_houdini.rule=Host(`houdini.${PROJECT_BASE_URL}`)" homarus: @@ -158,7 +145,7 @@ services: - ./jwt:/opt/jwt - ./config/crayfish/php.ini:/usr/local/etc/php/php.ini #labels: - # - "traefik.http.routers.${PROJECT_NAME}_homarus.rule=Host(`homarus.${PROJECT_BASE_URL}`)" + # - "traefik.http.routers.${PROJECT_NAME}_homarus.rule=Host(`homarus.${PROJECT_BASE_URL}`)" hypercube: @@ -177,7 +164,7 @@ services: - ./jwt:/opt/jwt - ./config/crayfish/php.ini:/usr/local/etc/php/php.ini #labels: - # - "traefik.http.routers.${PROJECT_NAME}_hypercube.rule=Host(`hypercube.${PROJECT_BASE_URL}`)" + # - "traefik.http.routers.${PROJECT_NAME}_hypercube.rule=Host(`hypercube.${PROJECT_BASE_URL}`)" crayfits: image: borndigital/isle-crayfits:mvp2-alpha @@ -193,7 +180,7 @@ services: volumes: - ./config/crayfish/php.ini:/usr/local/etc/php/php.ini #labels: - # - "traefik.http.routers.${PROJECT_NAME}_hypercube.rule=Host(`hypercube.${PROJECT_BASE_URL}`)" + # - "traefik.http.routers.${PROJECT_NAME}_hypercube.rule=Host(`hypercube.${PROJECT_BASE_URL}`)" fits: image: harvardlts/fitsservlet_container:1.5.0 @@ -213,11 +200,7 @@ services: traefik: # review https://hub.docker.com/_/traefik image: traefik:2.1.3 - container_name: "${PROJECT_NAME}_traefik" command: --api.insecure=true --providers.docker - networks: - internal: - external: ports: - '80:80' #- "443:443" @@ -230,7 +213,7 @@ services: # SSL Choice 2: To use commercial SSLs - uncomment ONLY the line below. Add your SSL certs (.cert, .pem, .key) files to config/traefik/ssl-certs # - ./config/traefik/ssl-certs:/certs:ro - # Use Environment vaiables to pass in Traefik config; no traefik.yml required + # Use Environment variables to pass in Traefik config; no traefik.yml required # by providers # Alternative to a static configureation /etc/traefik/traefik.yml" # Pass in config via flags or environment variables @@ -256,12 +239,20 @@ services: - activemq-data:/opt/activemq/data -networks: - internal: - external: +# networks: +# internal: +# external: volumes: + # For local database storage persistance we bind mount a local folder. + # This folder needs to be created prior to docker-compose up or build. mariadb-data: + driver: local + driver_opts: + type: none + o: bind + device: ./mariadb_files solr-data: activemq-data: - # isle-dc-postgres-data # Added to prototype but not currently used + # solr-data: + # isle-dc-postgres-data # Added to prototype but not currently used diff --git a/drupal.Dockerfile b/drupal.Dockerfile index bbab7e8db..ee5c4748f 100644 --- a/drupal.Dockerfile +++ b/drupal.Dockerfile @@ -1,5 +1,5 @@ ARG build_environment=prod -ARG code_dir=./code +ARG code_dir=./codebase ARG base_image_tag=latest ARG composer_version=1.9.3 ARG templates_dir=./config @@ -68,12 +68,14 @@ ARG app_runner_user_id=1000 ARG app_runner_group=drupal ARG app_runner_group_id=1000 ARG app_bin_dir=./scripts/app_bin +ARG NGINX_LISTEN_PORT=8080 +ARG NGINX_SERVER_ROOT ENV PATH=${PATH}:${APP_ROOT}/vendor/bin \ PHP_EXPOSE_PHP=Off \ PHP_FPM_USER=${app_runner_user} \ PHP_FPM_GROUP=${app_runner_group} \ - NGINX_LISTEN_PORT=8080 \ + NGINX_LISTEN_PORT=${NGINX_LISTEN_PORT} \ DEFAULT_USER=${app_runner_user} \ APP_NAME=drupal \ AUTO_INSTALL=${auto_install:-false} \ From 92ab0fc07d4aeee2d56c2e316848051f6fc73397 Mon Sep 17 00:00:00 2001 From: nikathone Date: Tue, 25 Feb 2020 16:52:39 -0500 Subject: [PATCH 048/107] Adjust auto install flag. --- docker-compose.yml | 8 ++++---- drupal.Dockerfile | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index dd0f620ea..f078cee73 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -60,8 +60,8 @@ services: DB_HOST: "mariadb" DB_PORT: ${DB_PORT:-3306} DB_DRIVER: ${DB_DRIVER:-mysql} - DRUSH_OPTIONS_URI: ${DRUSH_OPTIONS_URI} - NGINX_LISTEN_PORT: 8080 + DRUSH_OPTIONS_URI: ${PROJECT_BASE_URL:-islandora.localhost} + NGINX_LISTEN_PORT: "8080" volumes: # set delegated mode here on docker for mac for faster disk I/O - ./codebase:${APP_ROOT:-/var/www/app}:delegated @@ -202,7 +202,7 @@ services: image: traefik:2.1.3 command: --api.insecure=true --providers.docker ports: - - '80:80' + - '8000:80' #- "443:443" #- "8080:8080" volumes: @@ -251,7 +251,7 @@ volumes: driver_opts: type: none o: bind - device: ./mariadb_files + device: ${PWD}/mariadb_files solr-data: activemq-data: # solr-data: diff --git a/drupal.Dockerfile b/drupal.Dockerfile index ee5c4748f..499ff2f76 100644 --- a/drupal.Dockerfile +++ b/drupal.Dockerfile @@ -3,7 +3,7 @@ ARG code_dir=./codebase ARG base_image_tag=latest ARG composer_version=1.9.3 ARG templates_dir=./config -ARG auto_install=false +ARG auto_install # # Stage 1: PHP Dependencies From edcd5d6215456816f6a1c3857ec691bf7e15c7a0 Mon Sep 17 00:00:00 2001 From: nikathone Date: Tue, 25 Feb 2020 17:16:35 -0500 Subject: [PATCH 049/107] Added a drupal local url env var. --- docker-compose.yml | 6 +++--- drupal.Dockerfile | 4 +--- php.env | 3 ++- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index f078cee73..1e3b01e4a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,7 +26,6 @@ services: target: dev args: - code_dir=./codebase - - auto_install=true - templates_dir=./config - app_bin_dir=./scripts/app_bin - base_image_tag=latest @@ -60,8 +59,9 @@ services: DB_HOST: "mariadb" DB_PORT: ${DB_PORT:-3306} DB_DRIVER: ${DB_DRIVER:-mysql} - DRUSH_OPTIONS_URI: ${PROJECT_BASE_URL:-islandora.localhost} + DRUSH_OPTIONS_URI: ${DRUPAL_LOCAL_URL:-islandora.localhost} NGINX_LISTEN_PORT: "8080" + AUTO_INSTALL: "true" volumes: # set delegated mode here on docker for mac for faster disk I/O - ./codebase:${APP_ROOT:-/var/www/app}:delegated @@ -75,7 +75,7 @@ services: - ${APP_DOCROOT:-/var/www/app/web}/profiles/contrib - ${APP_DOCROOT:-/var/www/app/web}/libraries labels: - - "traefik.http.routers.${APP_NAME:-islandora}.rule=Host(`${PROJECT_BASE_URL:-islandora.localhost}`)" + - "traefik.http.routers.${APP_NAME:-islandora}.rule=Host(`${DRUPAL_LOCAL_URL:-islandora.localhost}`)" - "traefik.http.services.${APP_NAME:-islandora}.loadbalancer.server.port=8080" # solr: diff --git a/drupal.Dockerfile b/drupal.Dockerfile index 499ff2f76..980dd28d6 100644 --- a/drupal.Dockerfile +++ b/drupal.Dockerfile @@ -3,7 +3,6 @@ ARG code_dir=./codebase ARG base_image_tag=latest ARG composer_version=1.9.3 ARG templates_dir=./config -ARG auto_install # # Stage 1: PHP Dependencies @@ -61,7 +60,6 @@ RUN set -eux; \ # FROM registry.gitlab.com/nikathone/drupal-docker-good-defaults/php-nginx:${base_image_tag} as base ARG code_dir -ARG auto_intall ARG templates_dir ARG app_runner_user=drupal ARG app_runner_user_id=1000 @@ -78,7 +76,7 @@ ENV PATH=${PATH}:${APP_ROOT}/vendor/bin \ NGINX_LISTEN_PORT=${NGINX_LISTEN_PORT} \ DEFAULT_USER=${app_runner_user} \ APP_NAME=drupal \ - AUTO_INSTALL=${auto_install:-false} \ + AUTO_INSTALL=${AUTO_INSTALL:-false} \ APP_RUNNER_USER=${app_runner_user} \ APP_RUNNER_USER_ID=${app_runner_user_id:-1000} \ APP_RUNNER_GROUP=${app_runner_group} \ diff --git a/php.env b/php.env index e69f9e236..f7c558b4a 100644 --- a/php.env +++ b/php.env @@ -12,4 +12,5 @@ DRUPAL_USER=islandora DRUPAL_SITE_NAME=ISLE 8 Local DRUPAL_SITE_MAIL=admin@example.com DRUPAL_USER_PASSWORD=islandora -DRUPAL_USER_EMAIL=islandora@example.com \ No newline at end of file +DRUPAL_USER_EMAIL=islandora@example.com +DRUPAL_LOCAL_URL=islandora.localhost From 5b2f5c934b2c043a5642bb8c9375053d2c447b52 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 26 Feb 2020 16:34:39 -0500 Subject: [PATCH 050/107] Switch the auto install to false. --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 1e3b01e4a..452b335e9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -61,7 +61,7 @@ services: DB_DRIVER: ${DB_DRIVER:-mysql} DRUSH_OPTIONS_URI: ${DRUPAL_LOCAL_URL:-islandora.localhost} NGINX_LISTEN_PORT: "8080" - AUTO_INSTALL: "true" + AUTO_INSTALL: "false" volumes: # set delegated mode here on docker for mac for faster disk I/O - ./codebase:${APP_ROOT:-/var/www/app}:delegated From de4673d42feb0a253e904c937e9a343115348de9 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 26 Feb 2020 16:37:46 -0500 Subject: [PATCH 051/107] Making a force push for the config app runner script. --- scripts/app_bin/config_app_runner_user | 33 ++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 scripts/app_bin/config_app_runner_user diff --git a/scripts/app_bin/config_app_runner_user b/scripts/app_bin/config_app_runner_user new file mode 100644 index 000000000..02c5b445f --- /dev/null +++ b/scripts/app_bin/config_app_runner_user @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +set -e + +if [[ -n "${DEBUG}" ]]; then + set -x +fi + +# Adjust permission so that the regular user own the default home dir +chown ${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /home +# Make sure that app runner user can add supervisord and php-fpm sockets into /var/run folder +chown ${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /var/run && chmod g+w /var/run + +if [[ "${DEFAULT_USER}" == "${APP_RUNNER_USER}" ]]; then + echo "[info] The \"user\" directive makes sense only if the master process runs with super-user privileges" + sed -i -e '/user/!b' -e '/'${NGINX_USER}'/!b' -e '/'${NGINX_USER}'/d' /etc/nginx/nginx.conf + + # Make sure that every php-fpm conf file in /usr/local/etc/php-fpm.d/ doesn't have user or group directive enabled + for php_fpm_conf in /usr/local/etc/php-fpm.d/*.conf; do + sed -i 's/^user = /;&/' ${php_fpm_conf} + sed -i 's/^group = /;&/' ${php_fpm_conf} + done +fi + +# Allow app runner user to write into nginx logs +chown -R root:${APP_RUNNER_GROUP} /var/log/nginx +chmod -R g+w /var/log/nginx + +# Override native user and use the "_www" one created in the image +uid=$(stat -c %u /srv) +gid=$(stat -c %g /srv) +sed -i -r "s/${APP_RUNNER_USER}:x:\d+:\d+:/${APP_RUNNER_GROUP}:x:$uid:$gid:/g" /etc/passwd +sed -i -r "s/${APP_RUNNER_GROUP}:x:\d+:/${APP_RUNNER_GROUP}:x:$gid:/g" /etc/group From 2f1d827668c82f1fb8a7d179daafb6c6485437f3 Mon Sep 17 00:00:00 2001 From: nikathone Date: Fri, 28 Feb 2020 18:18:35 -0500 Subject: [PATCH 052/107] Setting back auto install to true and using data folder for drupal database and files folders. --- docker-compose.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 452b335e9..d3e60d4a6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -61,11 +61,11 @@ services: DB_DRIVER: ${DB_DRIVER:-mysql} DRUSH_OPTIONS_URI: ${DRUPAL_LOCAL_URL:-islandora.localhost} NGINX_LISTEN_PORT: "8080" - AUTO_INSTALL: "false" + AUTO_INSTALL: "true" volumes: # set delegated mode here on docker for mac for faster disk I/O - ./codebase:${APP_ROOT:-/var/www/app}:delegated - - ./drupal_files:${FILES_DIR:-/mnt/files} + - ./data/drupal/files:${FILES_DIR:-/mnt/files} # Anonymous volumes as a workaround to prevent empty or non existent host # folders from accidentally getting mounted in container and overwrite the # composer build stage files. @@ -251,7 +251,7 @@ volumes: driver_opts: type: none o: bind - device: ${PWD}/mariadb_files + device: ${PWD}/data/drupal/database solr-data: activemq-data: # solr-data: From 2165831d83c4595ed8a075a1c6a1993076b8498f Mon Sep 17 00:00:00 2001 From: nikathone Date: Fri, 28 Feb 2020 18:22:39 -0500 Subject: [PATCH 053/107] Checking for the settings.isle.php file instead of the app name specific. --- scripts/app_bin/docker-webserver-entrypoint | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/app_bin/docker-webserver-entrypoint b/scripts/app_bin/docker-webserver-entrypoint index 3a5548109..4dfa73189 100644 --- a/scripts/app_bin/docker-webserver-entrypoint +++ b/scripts/app_bin/docker-webserver-entrypoint @@ -28,7 +28,7 @@ file_env() { unset "$fileVar" } -if [[ -f "${APP_DOCROOT}/sites/default/settings.${APP_NAME}.php" ]]; then +if [[ -f "${APP_DOCROOT}/sites/default/settings.isle.php" ]]; then file_env "APP_NAME" file_env "APP_ACCOUNT_PASS" file_env "DB_NAME" From 4e2cbee5020762893bcbe0634f044696b3ed92cd Mon Sep 17 00:00:00 2001 From: nikathone Date: Fri, 28 Feb 2020 18:23:33 -0500 Subject: [PATCH 054/107] Added a script to init drupal codebase and setup settings.php --- Makefile | 48 ++++++++++++++ config/drupal/settings.isle.php | 42 ++++++++++++ config/drupal/snippet.txt | 7 ++ scripts/drupal/init.sh | 114 ++++++++++++++++++++++++++++++++ 4 files changed, 211 insertions(+) create mode 100644 Makefile create mode 100644 config/drupal/settings.isle.php create mode 100644 config/drupal/snippet.txt create mode 100755 scripts/drupal/init.sh diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..b5f532419 --- /dev/null +++ b/Makefile @@ -0,0 +1,48 @@ +# The variable used to determine which composer create project to use. +# Run make drupal_init_help for more information +isle_codebase ?= islandora + +docker_compose_project ?= islandora + +.PHONY: help drupal_init up build down down_rmi_all down_rmi_local drupal_clean clean_local clean + +default: drupal_init up + +help: + ./scripts/drupal/init.sh --help + +drupal_init: + ./scripts/drupal/init.sh --codebase $(isle_codebase) + +up: + docker-compose -p $(docker_compose_project) up --remove-orphans --detach + +build: + docker-compose -p $(docker_compose_project) up \ + --build \ + --detach \ + --remove-orphans + +down: + docker-compose -p $(docker_compose_project) down --remove-orphans + +down_rmi_all: + docker-compose -p $(docker_compose_project) down \ + --rmi all \ + --volumes \ + --remove-orphans + +down_rmi_local: + docker-compose -p $(docker_compose_project) down \ + --rmi local \ + --volumes \ + --remove-orphans + +# @todo we might chmod here since drupal set settings.php to readonly after +# install +drupal_clean: + chmod u+w codebase/web/sites/default && rm -rf codebase data/drupal + +clean_local: down_rmi_local drupal_clean + +clean: down_rmi_all drupal_clean diff --git a/config/drupal/settings.isle.php b/config/drupal/settings.isle.php new file mode 100644 index 000000000..f99ff7e5f --- /dev/null +++ b/config/drupal/settings.isle.php @@ -0,0 +1,42 @@ + getenv('DB_NAME'), + 'username' => getenv('DB_USER'), + 'password' => getenv('DB_PASSWORD'), + 'host' => getenv('DB_HOST'), + 'port' => getenv('DB_PORT'), + 'driver' => 'mysql', + 'namespace' => 'Drupal\\Core\\Database\\Driver\\mysql', + 'prefix' => '', + 'collation' => 'utf8mb4_general_ci', +]; + +/** + * Files path settings. + */ +$settings['file_public_path'] = 'sites/default/files'; +$settings['file_private_path'] = getenv('FILES_DIR') . '/private'; +$settings['file_temporary_path'] = '/tmp'; + +/** + * Hash salt setting. + */ +if (empty($settings['hash_salt'])) { + $settings['hash_salt'] = getenv('DRUPAL_HASH_SALT', true) ?: getenv('DRUPAL_HASH_SALT'); +} + +/** + * Config directory setting. + */ +$settings['config_sync_directory'] = '../config/sync'; diff --git a/config/drupal/snippet.txt b/config/drupal/snippet.txt new file mode 100644 index 000000000..0f1282a7a --- /dev/null +++ b/config/drupal/snippet.txt @@ -0,0 +1,7 @@ + +/** + * Load isle override configuration, if available. + */ +if (file_exists($app_root . '/' . $site_path . '/settings.isle.php')) { + include $app_root . '/' . $site_path . '/settings.isle.php'; +} diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh new file mode 100755 index 000000000..b0b79d28e --- /dev/null +++ b/scripts/drupal/init.sh @@ -0,0 +1,114 @@ +#!/usr/bin/env bash + +set -e + +# Print the number of arguments. +# echo "$#" + +codebase="drupal" +config_dir="$PWD/config/drupal" +scripts_dir="$PWD/scripts/drupal" + +function fail { + echo -e "\033[31m[ERROR]\033[0m $1" >&2 + exit 1 +} + +function help() { + echo "This command create the codebase folder using composer create-project based on drupal/recommended-project or islandora/drupal-project" + echo " " + echo "options:" + echo "-h, --help show brief help" + echo "-c, --codebase CODEBASE specify a codebase to use. drupal or islandora are the valid option" + exit 0 +} + +function install_drupal() { + local composer=$(command -v composer) + local args="create-project drupal/recommended-project:^8.8 codebase" + local flags="--ignore-platform-reqs --no-interaction" + local codebase="$1" + local drush_require="require drush/drush $flags" + + if [[ "$codebase" == "islandora" ]]; then + local args="create-project islandora/drupal-project codebase" + fi + + echo -e "\033[1m[INFO]\033[0m Installing drupal using composer" + echo " " + if [[ composer ]]; then + echo " You have composer installed locally and we are using it to download the drupal project." + echo >&2 + $composer $args $flags + echo " Adding drush to the drupal repository." + echo >&2 + cd codebase && $composer $drush_require && cd .. + elif [[ ! composer ]]; then + echo " Using the official composer docker image to download drupal." + mkdir -p $HOME/.composer && docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3 $composer_cmd_arguments $composer_cmd_flags + echo " Adding drush to the drupal repository." + echo >&2 + cd codebase && docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3 $drush_require && cd .. + else + fail "We could not download drupal. Please check your arguments and try again." + fi +} + +function setup_settings_isle_php() { + local default_dir="$PWD/codebase/web/sites/default" + local settings="${default_dir}/settings.php" + local settings_default="${default_dir}/default.settings.php" + local settings_isle="settings.isle.php" + local insert_after="\$settings\[\'entity_update_backup\'\] \= TRUE\;" + local snippet="${config_dir}/snippet.txt" + + if [[ -f "${default_dir}/${settings_isle}" ]]; then + fail "An existing settings.isle.php was found under ${default_dir}." + fi + + echo -e "\033[1m[INFO]\033[0m Setting up settings.isle.php to be included in settings.php" + echo " " + if [[ ! -f "${settings}" ]]; then + echo " An existing settings.php file couldn't be located and one is created from default.settings.php." + cp ${settings_default} ${settings} + else + echo " An existing settings.php file was found." + fi + + sed -i '' -e "/${insert_after}/r ${snippet}" ${settings} + cp "${config_dir}/${settings_isle}" "${default_dir}/${settings_isle}" + echo " settings.isle.php was successfully setup ." + echo >&2 +} + +while [ ! $# -eq 0 ] +do + case "$1" in + --help | -h) + help + exit + ;; + --debug | -d) + set -x + ;; + --codebase | -c) + shift + codebase=$1 + if [[ "$codebase" != 'drupal' && "$codebase" != 'islandora' ]]; then + fail "codebase:$codebase is unsupported. Only vanilla drupal or islandora can be downloaded." + fi + ;; + esac + shift +done + +# Checking if the codebase directory exists and quit. +if [[ -d "$PWD/codebase" && "$(ls -A $PWD/codebase)" ]]; then + fail "The codebase directory exists and is not empty. Please delete it and run this command again." +fi + +# Initialize drupal database and files persistent storage folders. +mkdir -p $PWD/data/drupal/files/public $PWD/data/drupal/files/private $PWD/data/drupal/database + +install_drupal $codebase +setup_settings_isle_php From d6ce5273d06027dc8b3e3da69b4f2d9a98823a0a Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 15:30:58 -0400 Subject: [PATCH 055/107] Removed drupal, php and nginx config confd templates since they will be now copied from the base image. --- config/drupal/confd/conf.d/app.env.toml | 3 - config/drupal/confd/templates/app.env.tmpl | 24 - config/drupal/load.environment.php | 15 + config/drupal/settings.isle.php | 4 +- config/nginx/conf.d/nginx.conf.toml | 3 - config/nginx/conf.d/vhost.conf.toml | 3 - config/nginx/templates/nginx.conf.tmpl | 44 -- config/nginx/templates/vhost.conf.tmpl | 78 ---- .../php/conf.d/docker-php-ext-apcu.ini.toml | 3 - .../conf.d/docker-php-ext-opcache.ini.toml | 3 - .../php/conf.d/docker-php-ext-xdebug.ini.toml | 3 - config/php/conf.d/docker-php.ini.toml | 3 - config/php/conf.d/zz-www.conf.toml | 3 - .../templates/docker-php-ext-apcu.ini.tmpl | 4 - .../templates/docker-php-ext-opcache.ini.tmpl | 9 - .../templates/docker-php-ext-xdebug.ini.tmpl | 26 -- config/php/templates/docker-php.ini.tmpl | 422 ------------------ config/php/templates/zz-www.conf.tmpl | 70 --- 18 files changed, 17 insertions(+), 703 deletions(-) delete mode 100644 config/drupal/confd/conf.d/app.env.toml delete mode 100644 config/drupal/confd/templates/app.env.tmpl create mode 100644 config/drupal/load.environment.php delete mode 100644 config/nginx/conf.d/nginx.conf.toml delete mode 100644 config/nginx/conf.d/vhost.conf.toml delete mode 100644 config/nginx/templates/nginx.conf.tmpl delete mode 100644 config/nginx/templates/vhost.conf.tmpl delete mode 100644 config/php/conf.d/docker-php-ext-apcu.ini.toml delete mode 100644 config/php/conf.d/docker-php-ext-opcache.ini.toml delete mode 100644 config/php/conf.d/docker-php-ext-xdebug.ini.toml delete mode 100644 config/php/conf.d/docker-php.ini.toml delete mode 100644 config/php/conf.d/zz-www.conf.toml delete mode 100644 config/php/templates/docker-php-ext-apcu.ini.tmpl delete mode 100644 config/php/templates/docker-php-ext-opcache.ini.tmpl delete mode 100644 config/php/templates/docker-php-ext-xdebug.ini.tmpl delete mode 100644 config/php/templates/docker-php.ini.tmpl delete mode 100644 config/php/templates/zz-www.conf.tmpl diff --git a/config/drupal/confd/conf.d/app.env.toml b/config/drupal/confd/conf.d/app.env.toml deleted file mode 100644 index 77413f359..000000000 --- a/config/drupal/confd/conf.d/app.env.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "app.env.tmpl" -dest = "/var/www/app/.env" diff --git a/config/drupal/confd/templates/app.env.tmpl b/config/drupal/confd/templates/app.env.tmpl deleted file mode 100644 index 3235d2261..000000000 --- a/config/drupal/confd/templates/app.env.tmpl +++ /dev/null @@ -1,24 +0,0 @@ -### APP SETTINGS - -APP_NAME={{ getenv "APP_NAME" "drupal" }} -APP_TITLE={{ getenv "APP_TITLE" "drupal" }} -APP_ACCOUNT_NAME={{ getenv "APP_ACCOUNT_NAME" "admin" }} -APP_ACCOUNT_MAIL={{ getenv "APP_ACCOUNT_MAIL" "drupal@example.com" }} -APP_ENV={{ getenv "APP_ENV" "local" }} - -DB_NAME={{ getenv "DB_NAME" "drupal" }} -DB_USER={{ getenv "DB_USER" "drupal" }} -DB_PASSWORD={{ getenv "DB_PASSWORD" "dbpassword" }} -DB_HOST={{ getenv "DB_HOST" "mariadb" }} -DB_DRIVER={{ getenv "DB_DRIVER" "mysql" }} -DB_PORT={{ getenv "DB_PORT" "3306" }} - -### Stack SETTINGS - -APP_ROOT={{ getenv "APP_ROOT" "/var/www/app" }} -APP_DOCROOT={{ getenv "APP_DOCROOT" "/var/www/app/web" }} -FILES_DIR={{ getenv "FILES_DIR" "/mnt/files" }} - -### Drush - -DRUSH_OPTIONS_URI={{ getenv "DRUSH_OPTIONS_URI" "" }} diff --git a/config/drupal/load.environment.php b/config/drupal/load.environment.php new file mode 100644 index 000000000..eef1d28d0 --- /dev/null +++ b/config/drupal/load.environment.php @@ -0,0 +1,15 @@ +safeLoad(); diff --git a/config/drupal/settings.isle.php b/config/drupal/settings.isle.php index f99ff7e5f..7fca57106 100644 --- a/config/drupal/settings.isle.php +++ b/config/drupal/settings.isle.php @@ -4,7 +4,7 @@ /** * @file - * Islandora settings override for drupal 8. + * Isle settings override for drupal 8. */ /** @@ -39,4 +39,4 @@ /** * Config directory setting. */ -$settings['config_sync_directory'] = '../config/sync'; +// $settings['config_sync_directory'] = '../config/sync'; diff --git a/config/nginx/conf.d/nginx.conf.toml b/config/nginx/conf.d/nginx.conf.toml deleted file mode 100644 index 3e968ea8b..000000000 --- a/config/nginx/conf.d/nginx.conf.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "nginx.conf.tmpl" -dest = "/etc/nginx/nginx.conf" diff --git a/config/nginx/conf.d/vhost.conf.toml b/config/nginx/conf.d/vhost.conf.toml deleted file mode 100644 index f7d52405e..000000000 --- a/config/nginx/conf.d/vhost.conf.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "vhost.conf.tmpl" -dest = "/etc/nginx/conf.d/default.conf" diff --git a/config/nginx/templates/nginx.conf.tmpl b/config/nginx/templates/nginx.conf.tmpl deleted file mode 100644 index 0e393fc8e..000000000 --- a/config/nginx/templates/nginx.conf.tmpl +++ /dev/null @@ -1,44 +0,0 @@ -user {{ getenv "NGINX_USER" "www-data" }}; - -worker_processes {{ getenv "NGINX_WORKER_PROCESSES" "auto" }}; -error_log /var/log/nginx/error.log warn; -pid /var/run/nginx.pid; - -events { - worker_connections {{ getenv "NGINX_WORKER_CONNECTIONS" "1024" }}; - multi_accept {{ getenv "NGINX_MULTI_ACCEPT" "on" }}; -} - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - proxy_temp_path /tmp/proxy_temp; - client_body_temp_path /tmp/client_temp; - fastcgi_temp_path /tmp/fastcgi_temp; - uwsgi_temp_path /tmp/uwsgi_temp; - scgi_temp_path /tmp/scgi_temp; - - server_names_hash_bucket_size {{ getenv "NGINX_SERVER_NAMES_HASH_BUCKET_SIZE" "64" }}; - - client_max_body_size {{ getenv "NGINX_CLIENT_MAX_BODY_SIZE" "64m" }}; - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /var/log/nginx/access.log main; - - sendfile {{ getenv "NGINX_SENDFILE" "on" }}; - tcp_nopush {{ getenv "NGINX_TCP_NOPUSH" "on" }}; - tcp_nodelay {{ getenv "NGINX_TCP_NODELAY" "on" }}; - types_hash_max_size {{ getenv "NGINX_TYPES_HASH_MAX_SIZE" "2048" }}; - - keepalive_timeout {{ getenv "NGINX_KEEPALIVE_TIMEOUT" "75s" }}; - keepalive_requests {{ getenv "NGINX_KEEPALIVE_REQUESTS" "100" }}; - - server_tokens {{ getenv "NGINX_SERVER_TOKENS" "off" }}; - - gzip {{ getenv "NGINX_GZIP" "on" }}; - - include /etc/nginx/conf.d/*.conf; -} diff --git a/config/nginx/templates/vhost.conf.tmpl b/config/nginx/templates/vhost.conf.tmpl deleted file mode 100644 index c9ae55f05..000000000 --- a/config/nginx/templates/vhost.conf.tmpl +++ /dev/null @@ -1,78 +0,0 @@ -server { - listen {{ getenv "NGINX_LISTEN_PORT" "80" }} default_server{{ if getenv "NGINX_HTTP2" }} http2{{ end }}; - server_name {{ getenv "NGINX_SERVER_NAME" "default" }}; - - root {{ getenv "NGINX_SERVER_ROOT" "/var/www/app/web" }}; - index index.php index.html; - - location = /favicon.ico { - log_not_found off; - access_log {{ getenv "NGINX_STATIC_ACCESS_LOG" "off" }}; - } - - location = /robots.txt { - allow all; - log_not_found off; - access_log {{ getenv "NGINX_STATIC_ACCESS_LOG" "off" }}; - } - - location ~ \..*/.*\.php$ { - return 403; - } - - location ~ ^/sites/.*/private/ { - return 403; - } - - # Block access to scripts in site files directory - location ~ ^/sites/[^/]+/files/.*\.php$ { - deny all; - } - - # Allow "Well-Known URIs" as per RFC 5785 - location ~* ^/.well-known/ { - allow all; - } - - # Block access to "hidden" files and directories whose names begin with a - # period. This includes directories used by version control systems such - # as Subversion or Git to store control files. - location ~ (^|/)\. { - return 403; - } - - location / { - try_files $uri /index.php?$query_string; - } - - location @rewrite { - rewrite ^/(.*)$ /index.php?q=$1; - } - - location ~ '\.php$|^/update.php' { - fastcgi_split_path_info ^(.+?\.php)(|/.*)$; - include fastcgi_params; - # Block httpoxy attacks. See https://httpoxy.org/. - fastcgi_param HTTP_PROXY ""; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_param PATH_INFO $fastcgi_path_info; - fastcgi_param QUERY_STRING $query_string; - fastcgi_intercept_errors {{ getenv "NGINX_FASTCGI_INTERCEPT_ERRORS" "on" }}; - fastcgi_pass unix:/var/run/php-fpm.sock; - } - - # Fighting with Styles? This little gem is amazing. - location ~ ^/sites/.*/files/styles/ { - try_files $uri @rewrite; - } - - # Handle private files through Drupal. Private file's path can come - # with a language prefix. - location ~ ^(/[a-z\-]+)?/system/files/ { - try_files $uri /index.php?$query_string; - } - - location ~* ^(?:.+\.(?:htaccess|make|txt|engine|inc|info|install|module|profile|po|pot|sh|.*sql|test|theme|tpl(?:\.php)?|xtmpl)|code-style\.pl|/Entries.*|/Repository|/Root|/Tag|/Template)$ { - return 404; - } -} diff --git a/config/php/conf.d/docker-php-ext-apcu.ini.toml b/config/php/conf.d/docker-php-ext-apcu.ini.toml deleted file mode 100644 index 9cabe33fe..000000000 --- a/config/php/conf.d/docker-php-ext-apcu.ini.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "docker-php-ext-apcu.ini.tmpl" -dest = "/usr/local/etc/php/conf.d/docker-php-ext-apcu.ini" diff --git a/config/php/conf.d/docker-php-ext-opcache.ini.toml b/config/php/conf.d/docker-php-ext-opcache.ini.toml deleted file mode 100644 index 59c27c8ae..000000000 --- a/config/php/conf.d/docker-php-ext-opcache.ini.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "docker-php-ext-opcache.ini.tmpl" -dest = "/usr/local/etc/php/conf.d/docker-php-ext-opcache.ini" diff --git a/config/php/conf.d/docker-php-ext-xdebug.ini.toml b/config/php/conf.d/docker-php-ext-xdebug.ini.toml deleted file mode 100644 index f10c1d7f3..000000000 --- a/config/php/conf.d/docker-php-ext-xdebug.ini.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "docker-php-ext-xdebug.ini.tmpl" -dest = "/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini" diff --git a/config/php/conf.d/docker-php.ini.toml b/config/php/conf.d/docker-php.ini.toml deleted file mode 100644 index 22ab90dd7..000000000 --- a/config/php/conf.d/docker-php.ini.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "docker-php.ini.tmpl" -dest = "/usr/local/etc/php/conf.d/docker-php.ini" diff --git a/config/php/conf.d/zz-www.conf.toml b/config/php/conf.d/zz-www.conf.toml deleted file mode 100644 index 690939af5..000000000 --- a/config/php/conf.d/zz-www.conf.toml +++ /dev/null @@ -1,3 +0,0 @@ -[template] -src = "zz-www.conf.tmpl" -dest = "/usr/local/etc/php-fpm.d/zz-www.conf" diff --git a/config/php/templates/docker-php-ext-apcu.ini.tmpl b/config/php/templates/docker-php-ext-apcu.ini.tmpl deleted file mode 100644 index f406016f1..000000000 --- a/config/php/templates/docker-php-ext-apcu.ini.tmpl +++ /dev/null @@ -1,4 +0,0 @@ -[apcu] -extension=apcu.so -apc.shm_size = {{ getenv "PHP_APCU_SHM_SIZE" "128M" }} -apc.enable_cli = {{ getenv "PHP_APCU_ENABLE_CLI" "Off" }} diff --git a/config/php/templates/docker-php-ext-opcache.ini.tmpl b/config/php/templates/docker-php-ext-opcache.ini.tmpl deleted file mode 100644 index b132edbfa..000000000 --- a/config/php/templates/docker-php-ext-opcache.ini.tmpl +++ /dev/null @@ -1,9 +0,0 @@ -[opcache] -zend_extension = opcache.so -opcache.enable = {{ getenv "PHP_OPCACHE_ENABLE" "On" }} -opcache.enable_cli = {{ getenv "PHP_OPCACHE_ENABLE_CLI" "Off" }} -opcache.memory_consumption = {{ getenv "PHP_OPCACHE_MEMORY_CONSUMPTION" "128" }} -opcache.interned_strings_buffer = {{ getenv "PHP_OPCACHE_INTERNED_STRINGS_BUFFER" "8" }} -opcache.max_accelerated_files = {{ getenv "PHP_OPCACHE_MAX_ACCELERATED_FILES" "4096" }} -opcache.validate_timestamps = {{ getenv "PHP_OPCACHE_VALIDATE_TIMESTAMPS" "On" }} -opcache.revalidate_freq = {{ getenv "PHP_OPCACHE_REVALIDATE_FREQ" "2" }} diff --git a/config/php/templates/docker-php-ext-xdebug.ini.tmpl b/config/php/templates/docker-php-ext-xdebug.ini.tmpl deleted file mode 100644 index ab4692c38..000000000 --- a/config/php/templates/docker-php-ext-xdebug.ini.tmpl +++ /dev/null @@ -1,26 +0,0 @@ -{{ if getenv "PHP_XDEBUG" }} -[xdebug] -zend_extension = xdebug.so - -xdebug.coverage_enable = {{ getenv "PHP_XDEBUG_COVERAGE_ENABLE" "On" }} -xdebug.default_enable = {{ getenv "PHP_XDEBUG_DEFAULT_ENABLE" "On" }} - -xdebug.remote_enable = {{ getenv "PHP_XDEBUG_REMOTE_ENABLE" "On" }} -xdebug.remote_handler = {{ getenv "PHP_XDEBUG_REMOTE_HANDLER" "dbgp" }} -xdebug.remote_connect_back = {{ getenv "PHP_XDEBUG_REMOTE_CONNECT_BACK" "On" }} -xdebug.remote_host = {{ getenv "PHP_XDEBUG_REMOTE_HOST" "localhost" }} -xdebug.remote_port = {{ getenv "PHP_XDEBUG_REMOTE_PORT" "9000" }} -xdebug.remote_log = "{{ getenv "PHP_XDEBUG_REMOTE_LOG" "" }}" -xdebug.remote_autostart = {{ getenv "PHP_XDEBUG_REMOTE_AUTOSTART" "On" }} - -xdebug.profiler_enable = {{ getenv "PHP_XDEBUG_PROFILER_ENABLE" "Off" }} -xdebug.profiler_enable_trigger = {{ getenv "PHP_XDEBUG_PROFILER_ENABLE_TRIGGER" "On" }} -xdebug.profiler_enable_trigger_value = "{{ getenv "PHP_XDEBUG_PROFILER_ENABLE_TRIGGER_VALUE" "XDEBUG_PROFILE" }}" -xdebug.profiler_output_dir = {{ getenv "FILES_DIR" }}/xdebug/profiler -xdebug.profiler_output_name = {{ getenv "PHP_XDEBUG_PROFILER_OUTPUT_NAME" "cachegrind.out.%p" }} - -xdebug.idekey = {{ getenv "PHP_XDEBUG_IDEKEY" "PHPSTORM"}} - -xdebug.max_nesting_level = {{ getenv "PHP_XDEBUG_MAX_NESTING_LEVEL" "256" }} - -{{ end }} diff --git a/config/php/templates/docker-php.ini.tmpl b/config/php/templates/docker-php.ini.tmpl deleted file mode 100644 index 97f38521c..000000000 --- a/config/php/templates/docker-php.ini.tmpl +++ /dev/null @@ -1,422 +0,0 @@ -; This file is used to override the default php.ini values -; BASIC SETTINGS: $PHP_INI_DIR/php.ini -; PHP-FPM SETTINGS: /usr/local/etc/php-fpm.d/zz-www.conf - -[PHP] - -;;;;;;;;;;;;;;;;;;;; -; Language Options ; -;;;;;;;;;;;;;;;;;;;; - -; Output buffering is a mechanism for controlling how much output data -; (excluding headers and cookies) PHP should keep internally before pushing that -; data to the client. If your application's output exceeds this setting, PHP -; will send that data in chunks of roughly the size you specify. -; Turning on this setting and managing its maximum buffer size can yield some -; interesting side-effects depending on your application and web server. -; You may be able to send headers and cookies after you've already sent output -; through print or echo. You also may see performance benefits if your server is -; emitting less packets due to buffered output versus PHP streaming the output -; as it gets it. On production servers, 4096 bytes is a good setting for performance -; reasons. -; Note: Output buffering can also be controlled via Output Buffering Control -; functions. -; Possible Values: -; On = Enabled and buffer is unlimited. (Use with caution) -; Off = Disabled -; Integer = Enables the buffer and sets its maximum size in bytes. -; Note: This directive is hardcoded to Off for the CLI SAPI -; Default Value: Off -; Development Value: 4096 -; Production Value: 4096 -; http://php.net/output-buffering -output_buffering = {{ getenv "PHP_OUTPUT_BUFFERING" "4096" }} - -; Duration of time, in seconds for which to cache realpath information for a given -; file or directory. For systems with rarely changing files, consider increasing this -; value. -; http://php.net/realpath-cache-ttl -realpath_cache_ttl = {{ getenv "PHP_REALPATH_CACHE_TTL" "120" }} - -; URL rewriter function rewrites URL on the fly by using -; output buffer. You can set target tags by this configuration. -; "form" tag is special tag. It will add hidden input tag to pass values. -; Refer to session.trans_sid_tags for usage. -; Default Value: "form=" -; Development Value: "form=" -; Production Value: "form=" -url_rewriter.tags = "{{ getenv "PHP_URL_REWRITER_TAGS" "a=href,area=href,frame=src,input=src,form=fakeentry" }}" - -;;;;;;;;;;;;;;;;; -; Miscellaneous ; -;;;;;;;;;;;;;;;;; - -; Decides whether PHP may expose the fact that it is installed on the server -; (e.g. by adding its signature to the Web server header). It is no security -; threat in any way, but it makes it possible to determine whether you use PHP -; on your server or not. -; http://php.net/expose-php -expose_php = {{ getenv "PHP_EXPOSE_PHP" "On" }} - -;;;;;;;;;;;;;;;;;;; -; Resource Limits ; -;;;;;;;;;;;;;;;;;;; - -; Maximum execution time of each script, in seconds -; http://php.net/max-execution-time -; Note: This directive is hardcoded to 0 for the CLI SAPI -max_execution_time = {{ getenv "PHP_MAX_EXECUTION_TIME" "120" }} - -; Maximum amount of time each script may spend parsing request data. It's a good -; idea to limit this time on productions servers in order to eliminate unexpectedly -; long running scripts. -; Note: This directive is hardcoded to -1 for the CLI SAPI -; Default Value: -1 (Unlimited) -; Development Value: 60 (60 seconds) -; Production Value: 60 (60 seconds) -; http://php.net/max-input-time -max_input_time = {{ getenv "PHP_MAX_INPUT_TIME" "60" }} - -; How many GET/POST/COOKIE input variables may be accepted -max_input_vars = {{ getenv "PHP_MAX_INPUT_VARS" "1000" }} - -; Maximum amount of memory a script may consume (512MB) -; http://php.net/memory-limit -memory_limit = {{ getenv "PHP_MEMORY_LIMIT" "512M" }} - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; Error handling and logging ; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -; This directive informs PHP of which errors, warnings and notices you would like -; it to take action for. The recommended way of setting values for this -; directive is through the use of the error level constants and bitwise -; operators. The error level constants are below here for convenience as well as -; some common settings and their meanings. -; By default, PHP is set to take action on all errors, notices and warnings EXCEPT -; those related to E_NOTICE and E_STRICT, which together cover best practices and -; recommended coding standards in PHP. For performance reasons, this is the -; recommend error reporting setting. Your production server shouldn't be wasting -; resources complaining about best practices and coding standards. That's what -; development servers and development settings are for. -; Note: The php.ini-development file has this setting as E_ALL. This -; means it pretty much reports everything which is exactly what you want during -; development and early testing. -; -; Error Level Constants: -; E_ALL - All errors and warnings (includes E_STRICT as of PHP 5.4.0) -; E_ERROR - fatal run-time errors -; E_RECOVERABLE_ERROR - almost fatal run-time errors -; E_WARNING - run-time warnings (non-fatal errors) -; E_PARSE - compile-time parse errors -; E_NOTICE - run-time notices (these are warnings which often result -; from a bug in your code, but it's possible that it was -; intentional (e.g., using an uninitialized variable and -; relying on the fact it is automatically initialized to an -; empty string) -; E_STRICT - run-time notices, enable to have PHP suggest changes -; to your code which will ensure the best interoperability -; and forward compatibility of your code -; E_CORE_ERROR - fatal errors that occur during PHP's initial startup -; E_CORE_WARNING - warnings (non-fatal errors) that occur during PHP's -; initial startup -; E_COMPILE_ERROR - fatal compile-time errors -; E_COMPILE_WARNING - compile-time warnings (non-fatal errors) -; E_USER_ERROR - user-generated error message -; E_USER_WARNING - user-generated warning message -; E_USER_NOTICE - user-generated notice message -; E_DEPRECATED - warn about code that will not work in future versions -; of PHP -; E_USER_DEPRECATED - user-generated deprecation warnings -; -; Common Values: -; E_ALL (Show all errors, warnings and notices including coding standards.) -; E_ALL & ~E_NOTICE (Show all errors, except for notices) -; E_ALL & ~E_NOTICE & ~E_STRICT (Show all errors, except for notices and coding standards warnings.) -; E_COMPILE_ERROR|E_RECOVERABLE_ERROR|E_ERROR|E_CORE_ERROR (Show only errors) -; Default Value: E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED -; Development Value: E_ALL -; Production Value: E_ALL & ~E_DEPRECATED & ~E_STRICT -; http://php.net/error-reporting -error_reporting = {{ getenv "PHP_ERROR_REPORTING" "E_ALL & ~E_DEPRECATED & ~E_STRICT" }} - -; This directive controls whether or not and where PHP will output errors, -; notices and warnings too. Error output is very useful during development, but -; it could be very dangerous in production environments. Depending on the code -; which is triggering the error, sensitive information could potentially leak -; out of your application such as database usernames and passwords or worse. -; For production environments, we recommend logging errors rather than -; sending them to STDOUT. -; Possible Values: -; Off = Do not display any errors -; stderr = Display errors to STDERR (affects only CGI/CLI binaries!) -; On or stdout = Display errors to STDOUT -; Default Value: On -; Development Value: On -; Production Value: Off -; http://php.net/display-errors -display_errors = {{ getenv "PHP_DISPLAY_ERRORS" "Off" }} - -; The display of errors which occur during PHP's startup sequence are handled -; separately from display_errors. PHP's default behavior is to suppress those -; errors from clients. Turning the display of startup errors on can be useful in -; debugging configuration problems. We strongly recommend you -; set this to 'off' for production servers. -; Default Value: Off -; Development Value: On -; Production Value: Off -; http://php.net/display-startup-errors -display_startup_errors = {{ getenv "PHP_DISPLAY_STARTUP_ERRORS" "Off" }} - -; Besides displaying errors, PHP can also log errors to locations such as a -; server-specific log, STDERR, or a location specified by the error_log -; directive found below. While errors should not be displayed on productions -; servers they should still be monitored and logging is a great way to do that. -; Default Value: Off -; Development Value: On -; Production Value: On -; http://php.net/log-errors -log_errors = {{ getenv "PHP_LOG_ERRORS" "On" }} - -; Set maximum length of log_errors. In error_log information about the source is -; added. The default is 1024 and 0 allows to not apply any maximum length at all. -; http://php.net/log-errors-max-len -log_errors_max_len = {{ getenv "PHP_LOG_ERRORS_MAX_LEN" "1024" }} - -;;;;;;;;;;;;;;;;; -; Data Handling ; -;;;;;;;;;;;;;;;;; - -; Maximum size of POST data that PHP will accept. -; Its value may be 0 to disable the limit. It is ignored if POST data reading -; is disabled through enable_post_data_reading. -; http://php.net/post-max-size -post_max_size = {{ getenv "PHP_POST_MAX_SIZE" "32M" }} - -; Automatically add files before PHP document. -; http://php.net/auto-prepend-file -auto_prepend_file = {{ getenv "PHP_AUTO_PREPEND_FILE" }} - -; Automatically add files after PHP document. -; http://php.net/auto-append-file -auto_append_file = {{ getenv "PHP_AUTO_APPEND_FILE" }} - -;;;;;;;;;;;;;;;;;;;;;;;;; -; Paths and Directories ; -;;;;;;;;;;;;;;;;;;;;;;;;; - -; Determines the size of the realpath cache to be used by PHP. This value should -; be increased on systems where PHP opens many files to reflect the quantity of -; the file operations performed. -; Note: if open_basedir is set, the cache is disabled -; http://php.net/realpath-cache-size -realpath_cache_size = {{ getenv "PHP_REALPATH_CACHE_SIZE" "800K" }} - -;;;;;;;;;;;;;;;; -; File Uploads ; -;;;;;;;;;;;;;;;; - -; Maximum allowed size for uploaded files. -; http://php.net/upload-max-filesize -upload_max_filesize = {{ getenv "PHP_UPLOAD_MAX_FILESIZE" "100M" }} - -; Maximum number of files that can be uploaded via a single request -max_file_uploads = {{ getenv "PHP_MAX_FILE_UPLOADS" "20" }} - -;;;;;;;;;;;;;;;;;; -; Fopen wrappers ; -;;;;;;;;;;;;;;;;;; - -; Whether to allow the treatment of URLs (like http:// or ftp://) as files. -; http://php.net/allow-url-fopen -allow_url_fopen = {{ getenv "PHP_ALLOW_URL_FOPEN" "On" }} - -; Default timeout for socket based streams (seconds) -; http://php.net/default-socket-timeout -default_socket_timeout = {{ getenv "PHP_DEFAULT_SOCKET_TIMEOUT" "60" }} - -;;;;;;;;;;;;;;;;;;; -; Module Settings ; -;;;;;;;;;;;;;;;;;;; - -[Date] - -; Defines the default timezone used by the date functions -; http://php.net/date.timezone -date.timezone = {{ getenv "PHP_DATE_TIMEZONE" "America/Toronto"}} - -[Pdo_mysql] - -; If mysqlnd is used: Number of cache slots for the internal result set cache -; http://php.net/pdo_mysql.cache_size -pdo_mysql.cache_size = {{ getenv "PHP_PDO_MYSQL_CACHE_SIZE" "2000" }} - -[mail function] - -; For Unix only. You may supply arguments as well (default: "sendmail -t -i") -; or /usr/sbin/sendmail -t -i -; http://php.net/sendmail-path -sendmail_path = {{ getenv "PHP_SENDMAIL_PATH" "/bin/true" }} - -; Add X-PHP-Originating-Script: that will include uid of the script followed by the filename -mail.add_x_header = {{ getenv "PHP_MAIL_ADD_X_HEADER" "On" }} - -[ODBC] - -; Handling of binary data. 0 means passthru, 1 return as is, 2 convert to char. -; See the documentation on odbc_binmode and odbc_longreadlen for an explanation -; of odbc.defaultlrl and odbc.defaultbinmode -; http://php.net/odbc.defaultbinmode -odbc.defaultbinmode = 1 - -[MySQLi] - -; If mysqlnd is used: Number of cache slots for the internal result set cache -; http://php.net/mysqli.cache_size -mysqli.cache_size = {{ getenv "PHP_MYSQLI_CACHE_SIZE" "2000" }} - -[Session] - -; Handler used to store/retrieve data. -; http://php.net/session.save-handler -session.save_handler = {{ getenv "PHP_SESSION_SAVE_HANDLER" "files" }} -; Argument passed to save_handler. In the case of files, this is the path -; where data files are stored. Note: Windows users have to change this -; variable in order to use PHP's session functions. -; -; The path can be defined as: -; -; session.save_path = "N;/path" -; -; where N is an integer. Instead of storing all the session files in -; /path, what this will do is use subdirectories N-levels deep, and -; store the session data in those directories. This is useful if -; your OS has problems with many files in one directory, and is -; a more efficient layout for servers that handle many sessions. -; -; NOTE 1: PHP will not create this directory structure automatically. -; You can use the script in the ext/session dir for that purpose. -; NOTE 2: See the section on garbage collection below if you choose to -; use subdirectories for session storage -; -; The file storage module creates files using mode 600 by default. -; You can change that by using -; -; session.save_path = "N;MODE;/path" -; -; where MODE is the octal representation of the mode. Note that this -; does not overwrite the process's umask. -; http://php.net/session.save-path -session.save_path = "{{ getenv "PHP_SESSION_SAVE_PATH" "/tmp" }}" -; Whether to use cookies. -; http://php.net/session.use-cookies -session.use_cookies = {{ getenv "PHP_SESSION_USE_COOKIES" "1" }} -; This option forces PHP to fetch and use a cookie for storing and maintaining -; the session id. We encourage this operation as it's very helpful in combating -; session hijacking when not specifying and managing your own session id. It is -; not the be-all and end-all of session hijacking defense, but it's a good start. -; http://php.net/session.use-only-cookies -session.use_only_cookies = {{ getenv "PHP_SESSION_USE_ONLY_COOKIES" "1" }} -; Name of the session (used as cookie name). -; http://php.net/session.name -session.name = {{ getenv "PHP_SESSION_NAME" "PHPSESSID" }} -; Initialize session on request startup. -; http://php.net/session.auto-start -session.auto_start = {{ getenv "PHP_SESSION_AUTO_START" "0" }} - -; Lifetime in seconds of cookie or, if 0, until browser is restarted. -; http://php.net/session.cookie-lifetime -session.cookie_lifetime = {{ getenv "PHP_SESSION_COOKIE_LIFETIME" "2000" }} -; The path for which the cookie is valid. -; http://php.net/session.cookie-path -session.cookie_path = {{ getenv "PHP_SESSION_COOKIE_PATH" "/" }} -; The domain for which the cookie is valid. -; http://php.net/session.cookie-domain -session.cookie_domain = {{ getenv "PHP_SESSION_COOKIE_DOMAIN" }} -; Whether or not to add the httpOnly flag to the cookie, which makes it inaccessible to browser scripting languages such as JavaScript. -; http://php.net/session.cookie-httponly -session.cookie_httponly = {{ getenv "PHP_SESSION_COOKIE_HTTPONLY" }} - -; Handler used to serialize data. php is the standard serializer of PHP. -; http://php.net/session.serialize-handler -session.serialize_handler = {{ getenv "PHP_SESSION_SERIALIZE_HANDLER" "php" }} - -; Defines the probability that the 'garbage collection' process is started -; on every session initialization. The probability is calculated by using -; gc_probability/gc_divisor. Where session.gc_probability is the numerator -; and gc_divisor is the denominator in the equation. Setting this value to 1 -; when the session.gc_divisor value is 100 will give you approximately a 1% chance -; the gc will run on any give request. -; Default Value: 1 -; Development Value: 1 -; Production Value: 1 -; http://php.net/session.gc-probability -session.gc_probability = {{ getenv "PHP_SESSION_GC_PROBABILITY" "1" }} -; Defines the probability that the 'garbage collection' process is started on every -; session initialization. The probability is calculated by using the following equation: -; gc_probability/gc_divisor. Where session.gc_probability is the numerator and -; session.gc_divisor is the denominator in the equation. Setting this value to 1 -; when the session.gc_divisor value is 100 will give you approximately a 1% chance -; the gc will run on any give request. Increasing this value to 1000 will give you -; a 0.1% chance the gc will run on any give request. For high volume production servers, -; this is a more efficient approach. -; Default Value: 100 -; Development Value: 1000 -; Production Value: 1000 -; http://php.net/session.gc-divisor -session.gc_divisor = {{ getenv "PHP_SESSION_GC_DIVISOR" "100" }} -; After this number of seconds, stored data will be seen as 'garbage' and -; cleaned up by the garbage collection process. -; http://php.net/session.gc-maxlifetime -session.gc_maxlifetime = {{ getenv "PHP_SESSION_GC_MAXLIFETIME" "1440" }} - -; Check HTTP Referer to invalidate externally stored URLs containing ids. -; HTTP_REFERER has to contain this substring for the session to be -; considered as valid. -; http://php.net/session.referer-check -session.referer_check = {{ getenv "PHP_SESSION_REFERER_CHECK" "" }} - -; Set to {nocache,private,public,} to determine HTTP caching aspects -; or leave this empty to avoid sending anti-caching headers. -; http://php.net/session.cache-limiter -session.cache_limiter = {{ getenv "PHP_SESSION_CACHE_LIMITER" "nocache" }} -; Document expires after n minutes. -; http://php.net/session.cache-expire -session.cache_expire = {{ getenv "PHP_SESSION_CACHE_EXPIRE" "180" }} - -; trans sid support is disabled by default. -; Use of trans sid may risk your users' security. -; Use this option with caution. -; - User may send URL contains active session ID -; to other person via. email/irc/etc. -; - URL that contains active session ID may be stored -; in publicly accessible computer. -; - User may access your site with the same session ID -; always using URL stored in browser's history or bookmarks. -; http://php.net/session.use-trans-sid -session.use_trans_sid = {{ getenv "PHP_SESSION_USE_TRANS_SID" "0" }} - -; Set session ID character length. This value could be between 22 to 256. -; Shorter length than default is supported only for compatibility reason. -; Users should use 32 or more chars. -; http://php.net/session.sid-length -; Default Value: 32 -; Development Value: 26 -; Production Value: 26 -session.sid_length = {{ getenv "PHP_SESSION_SID_LENGTH" "32" }} - -; Define how many bits are stored in each character when converting -; the binary hash data to something readable. -; Possible values: -; 4 (4 bits: 0-9, a-f) -; 5 (5 bits: 0-9, a-v) -; 6 (6 bits: 0-9, a-z, A-Z, "-", ",") -; Default Value: 4 -; Development Value: 5 -; Production Value: 5 -; http://php.net/session.hash-bits-per-character -session.sid_bits_per_character = {{ getenv "PHP_SESSION_SID_BITS_PER_CHARACTER" "5" }} - -[Assertion] -zend.assertions = {{ getenv "PHP_ZEND_ASSERTIONS" "1" }} -assert.active = {{ getenv "PHP_ASSERT_ACTIVE" "On" }} diff --git a/config/php/templates/zz-www.conf.tmpl b/config/php/templates/zz-www.conf.tmpl deleted file mode 100644 index 1432222b3..000000000 --- a/config/php/templates/zz-www.conf.tmpl +++ /dev/null @@ -1,70 +0,0 @@ -[www] - -; Unix user/group of processes -; Note: The user is mandatory. If the group is not set, the default user's group -; will be used. -user = {{ getenv "PHP_FPM_USER" "www-data" }} -group = {{ getenv "PHP_FPM_GROUP" "www-data" }} - -listen = /var/run/php-fpm.sock -listen.owner = {{ getenv "PHP_FPM_USER" "www-data" }} -listen.group = {{ getenv "PHP_FPM_GROUP" "www-data" }} - -; Redirect worker stdout and stderr into main error log. If not set, stdout and -; stderr will be redirected to /dev/null according to FastCGI specs. -; Note: on highloaded environement, this can cause some delay in the page -; process time (several ms). -; Default Value: no -clear_env = {{ getenv "PHP_FPM_CLEAR_ENV" "yes" }} - -; Choose how the process manager will control the number of child processes. -; Possible Values: -; static - a fixed number (pm.max_children) of child processes; -; dynamic - the number of child processes are set dynamically based on the -; following directives. With this process management, there will be -; always at least 1 children. -; pm.max_children - the maximum number of children that can -; be alive at the same time. -; pm.start_servers - the number of children created on startup. -; pm.min_spare_servers - the minimum number of children in 'idle' -; state (waiting to process). If the number -; of 'idle' processes is less than this -; number then some children will be created. -; pm.max_spare_servers - the maximum number of children in 'idle' -; state (waiting to process). If the number -; of 'idle' processes is greater than this -; number then some children will be killed. -; ondemand - no children are created at startup. Children will be forked when -; new requests will connect. The following parameter are used: -; pm.max_children - the maximum number of children that -; can be alive at the same time. -; pm.process_idle_timeout - The number of seconds after which -; an idle process will be killed. -; Note: This value is mandatory. -pm = {{ getenv "PHP_FPM_PM" "dynamic" }} -; The number of child processes to be created when pm is set to 'static' and the -; maximum number of child processes when pm is set to 'dynamic' or 'ondemand'. -; This value sets the limit on the number of simultaneous requests that will be -; served. Equivalent to the ApacheMaxClients directive with mpm_prefork. -; Equivalent to the PHP_FCGI_CHILDREN environment variable in the original PHP -; CGI. The below defaults are based on a server without much resources. Don't -; forget to tweak pm.* to fit your needs. -; Note: Used when pm is set to 'static', 'dynamic' or 'ondemand' -; Note: This value is mandatory. Default docker is 5. -pm.max_children = {{ getenv "PHP_FPM_PM_MAX_CHILDREN" "20" }} -; The number of child processes created on startup. -; Note: Used only when pm is set to 'dynamic' -; Default Value: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 -pm.start_servers = {{ getenv "PHP_FPM_PM_START_SERVERS" "5" }} -; The desired minimum number of idle server processes. -; Note: Used and Mandatory only when pm is set to 'dynamic' -pm.min_spare_servers = {{ getenv "PHP_FPM_PM_MIN_SPARE_SERVERS" "5" }} -; The desired maximum number of idle server processes. -; Note: Used only when pm is set to 'dynamic' -; Note: Mandatory when pm is set to 'dynamic' -pm.max_spare_servers = {{ getenv "PHP_FPM_PM_MAX_SPARE_SERVERS" "5" }} -; The number of requests each child process should execute before respawning. -; This can be useful to work around memory leaks in 3rd party libraries. For -; endless request processing specify '0'. Equivalent to PHP_FCGI_MAX_REQUESTS. -; Default Value: 0 -pm.max_requests = {{ getenv "PHP_FPM_PM_MAX_REQUESTS" "500" }} From 77640c6da60e31b64760e3482033c47bb64a2841 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 15:32:44 -0400 Subject: [PATCH 056/107] Updated init.sh to include needed packages and removed app bin folder since now it will be copied from the base image. --- scripts/app_bin/config_app_runner_user | 33 ---- scripts/app_bin/docker-webserver-entrypoint | 53 ------- scripts/app_bin/files_link | 43 ------ scripts/app_bin/install_drupal | 63 -------- scripts/app_bin/wait_for_database | 37 ----- scripts/drupal/init.sh | 161 +++++++++++++++----- 6 files changed, 123 insertions(+), 267 deletions(-) delete mode 100644 scripts/app_bin/config_app_runner_user delete mode 100644 scripts/app_bin/docker-webserver-entrypoint delete mode 100644 scripts/app_bin/files_link delete mode 100644 scripts/app_bin/install_drupal delete mode 100644 scripts/app_bin/wait_for_database diff --git a/scripts/app_bin/config_app_runner_user b/scripts/app_bin/config_app_runner_user deleted file mode 100644 index 02c5b445f..000000000 --- a/scripts/app_bin/config_app_runner_user +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [[ -n "${DEBUG}" ]]; then - set -x -fi - -# Adjust permission so that the regular user own the default home dir -chown ${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /home -# Make sure that app runner user can add supervisord and php-fpm sockets into /var/run folder -chown ${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /var/run && chmod g+w /var/run - -if [[ "${DEFAULT_USER}" == "${APP_RUNNER_USER}" ]]; then - echo "[info] The \"user\" directive makes sense only if the master process runs with super-user privileges" - sed -i -e '/user/!b' -e '/'${NGINX_USER}'/!b' -e '/'${NGINX_USER}'/d' /etc/nginx/nginx.conf - - # Make sure that every php-fpm conf file in /usr/local/etc/php-fpm.d/ doesn't have user or group directive enabled - for php_fpm_conf in /usr/local/etc/php-fpm.d/*.conf; do - sed -i 's/^user = /;&/' ${php_fpm_conf} - sed -i 's/^group = /;&/' ${php_fpm_conf} - done -fi - -# Allow app runner user to write into nginx logs -chown -R root:${APP_RUNNER_GROUP} /var/log/nginx -chmod -R g+w /var/log/nginx - -# Override native user and use the "_www" one created in the image -uid=$(stat -c %u /srv) -gid=$(stat -c %g /srv) -sed -i -r "s/${APP_RUNNER_USER}:x:\d+:\d+:/${APP_RUNNER_GROUP}:x:$uid:$gid:/g" /etc/passwd -sed -i -r "s/${APP_RUNNER_GROUP}:x:\d+:/${APP_RUNNER_GROUP}:x:$gid:/g" /etc/group diff --git a/scripts/app_bin/docker-webserver-entrypoint b/scripts/app_bin/docker-webserver-entrypoint deleted file mode 100644 index 4dfa73189..000000000 --- a/scripts/app_bin/docker-webserver-entrypoint +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [[ -n "${DEBUG}" ]]; then - set -x -fi - -# usage: file_env VAR [DEFAULT] -# ie: file_env 'DB_PASSWORD' 'example' -# (will allow for "$DB_PASSWORD_FILE" to fill in the value of -# "$DB_PASSWORD" from a file, especially for Docker's secrets feature) -file_env() { - local var="$1" - local fileVar="${var}_FILE" - local def="${2:-}" - if [[ "${!var:-}" ]] && [[ "${!fileVar:-}" ]]; then - echo >&2 "error: both $var and $fileVar are set (but are exclusive)" - exit 1 - fi - local val="${def}" - if [[ "${!var:-}" ]]; then - val="${!var}" - elif [[ "${!fileVar:-}" ]]; then - val="$(< "${!fileVar}")" - fi - export "$var"="$val" - unset "$fileVar" -} - -if [[ -f "${APP_DOCROOT}/sites/default/settings.isle.php" ]]; then - file_env "APP_NAME" - file_env "APP_ACCOUNT_PASS" - file_env "DB_NAME" - file_env "DB_USER" - file_env "DB_PASSWORD" -fi - -# Make sure files folder is linked at the proper folder -/usr/local/bin/files_link "${APP_DOCROOT}/sites/default/files" - -# set custom config files for the drupal .env where the credentials are. -/usr/local/bin/confd -onetime -backend env - -# Delay container to start until the database server is ready. -/usr/local/bin/wait_for_database - -# Attempt to install drupal. -if [[ "${AUTO_INSTALL}" == "true" ]]; then - /usr/local/bin/install_drupal -fi - -exec "$@" diff --git a/scripts/app_bin/files_link b/scripts/app_bin/files_link deleted file mode 100644 index bbd10f417..000000000 --- a/scripts/app_bin/files_link +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [[ -n "${DEBUG}" ]]; then - set -x -fi - -app_public_dir=$1 - -# Add symlink from persistent files volume to application's storage public dir. -if [[ -n "${app_public_dir}" ]]; then - echo >&2 - echo >&2 "[INFO] Application's public storage directory specified, trying to symlink from files persistent volume" - echo >&2 - - if [[ -d "${app_public_dir}" ]]; then - if [[ ! -L "${app_public_dir}" ]]; then - if [[ "$(ls -A "${app_public_dir}")" ]]; then - echo >&2 - echo >&2 "[ERROR] Failed to symlink public storage directory to a persistent volume" - echo >&2 " Directory ${app_public_dir} must not exists or be empty" - echo >&2 " (use files import to migrate existing public files)" - echo >&2 - exit 1 - # If dir is not symlink and empty, remove it and link. - else - echo >&2 - echo >&2 "[INFO] Empty public storage dir detected: removing and symlinking '${app_public_dir}' -> ''${FILES_DIR}/public'" - echo >&2 - rm -rf "${app_public_dir}" - ln -sf "${FILES_DIR}/public" "${app_public_dir}" - fi - else - echo "Symlink already in place" - fi - else - echo >&2 - echo >&2 "[INFO] No public storage dir detected: just symlinking '${app_public_dir}' -> '${FILES_DIR}/public'" - echo >&2 - ln -sf "${FILES_DIR}/public" "${app_public_dir}" - fi -fi diff --git a/scripts/app_bin/install_drupal b/scripts/app_bin/install_drupal deleted file mode 100644 index 7de9198cf..000000000 --- a/scripts/app_bin/install_drupal +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [[ -n "${DEBUG}" ]]; then - set -x -fi - -if [[ "${APP_ENV}" == "dev" && ! -f "${APP_DOCROOT}/index.php" ]]; then - cd ${APP_ROOT} - composer install --classmap-authoritative -fi; - -# Ensure we are in the app web. -cd ${APP_DOCROOT} - -# Determine if site already installed -site_installed=$(${APP_ROOT}/vendor/bin/drush status bootstrap | grep -q Successful; echo "$?") - -if [[ -n "${site_installed}" && "${site_installed}" -eq 0 ]]; then - echo >&2 - echo >&2 '[INFO] Site already installed' - echo >&2 ' You might need to run ../vendor/bin/drush config-import if' - echo >&2 ' the database configurations are out of sync.' - echo >&2 -else - site_install_options=( - --yes - --verbose - --account-name=${APP_ACCOUNT_NAME} - --account-mail=${APP_ACCOUNT_MAIL} - --account-pass=${APP_ACCOUNT_PASS} - ) - if [[ "$(ls -A ${APP_ROOT}/config/sync)" && -f ${APP_ROOT}/config/sync/core.extension.yml ]]; then - echo >&2 - echo >&2 '[INFO] Installing site using existing configurations' - echo >&2 '[INFO] Switching to minimal profile to allow existing config to install.' - echo >&2 ' See https://www.drupal.org/node/2897299 for more information.' - echo >&2 - sed -i 's/standard:/minimal:/g' ${APP_ROOT}/config/sync/core.extension.yml - sed -i 's/profile: standard/profile: minimal/g' ${APP_ROOT}/config/sync/core.extension.yml - site_install_options+=( - --existing-config - minimal - ) - else - echo >&2 - echo >&2 '[INFO] Installing site from scratch' - echo >&2 - site_install_options+=( - --site-name=${APP_NAME} - --site-mail=${APP_ACCOUNT_MAIL} - standard - ) - fi - - if [[ -n "${DEBUG}" ]]; then - site_install_options+=( --debug ) - fi - - # Installing drupal - ${APP_ROOT}/vendor/bin/drush site:install "${site_install_options[@]}" -fi diff --git a/scripts/app_bin/wait_for_database b/scripts/app_bin/wait_for_database deleted file mode 100644 index 7f2da2cc2..000000000 --- a/scripts/app_bin/wait_for_database +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env bash - -set -e - -if [[ -n "${DEBUG}" ]]; then - set -x -fi - -function fail { - echo $1 >&2 - exit 1 -} - -function retry { - local n=1 - local max=5 - local delay=5 - while true; do - "$@" && break || { - if [[ $n -lt $max ]]; then - ((n++)) - echo "Command failed. Attempt $n/$max:" - sleep $delay; - else - fail "The command has failed after $n attempts." - fi - } - done -} - -# Check connection to the database and verify if the database is created -retry mysql --silent --quick --force \ - --host=${DB_HOST} \ - --port=${DB_PORT} \ - --user=${DB_USER} \ - --password=${DB_PASSWORD} \ - --execute "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME='${DB_NAME}'" >/dev/null 2>&1 diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index b0b79d28e..96a818911 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -8,6 +8,11 @@ set -e codebase="drupal" config_dir="$PWD/config/drupal" scripts_dir="$PWD/scripts/drupal" +composer_install_run="true" +current_folder="$PWD" +composer_general_flags="--ignore-platform-reqs --no-interaction" +OS=`uname -s` +[[ "$OS" == "Darwin" ]] && is_darwin=true || is_darwin=false function fail { echo -e "\033[31m[ERROR]\033[0m $1" >&2 @@ -23,62 +28,110 @@ function help() { exit 0 } -function install_drupal() { - local composer=$(command -v composer) - local args="create-project drupal/recommended-project:^8.8 codebase" +function download_drupal() { + local args="create-project drupal/recommended-project:^8.8" local flags="--ignore-platform-reqs --no-interaction" local codebase="$1" local drush_require="require drush/drush $flags" + if [[ ! $composer ]]; then + fail "We could not download drupal. Ensure composer or docker is setup and installed properly on your local host." + fi + if [[ "$codebase" == "islandora" ]]; then - local args="create-project islandora/drupal-project codebase" + local args="create-project islandora/drupal-project" fi echo -e "\033[1m[INFO]\033[0m Installing drupal using composer" echo " " - if [[ composer ]]; then - echo " You have composer installed locally and we are using it to download the drupal project." - echo >&2 - $composer $args $flags - echo " Adding drush to the drupal repository." - echo >&2 - cd codebase && $composer $drush_require && cd .. - elif [[ ! composer ]]; then - echo " Using the official composer docker image to download drupal." - mkdir -p $HOME/.composer && docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3 $composer_cmd_arguments $composer_cmd_flags - echo " Adding drush to the drupal repository." - echo >&2 - cd codebase && docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3 $drush_require && cd .. - else - fail "We could not download drupal. Please check your arguments and try again." + + echo " Downloading the drupal codebase." + echo >&2 + cd "$current_folder/" + $composer $args $composer_general_flags codebase + + download_required_packages +} + +function download_required_packages() { + # Using two arrays cause the default MacOS bash is an older version. Probably less than 4. + local packages=("zaporylie/composer-drupal-optimizations" "vlucas/phpdotenv" "drush/drush" "cweagans/composer-patches") + local versions=("^1.1 --dev" "^4.0" "^10.0" "^1.6.7") + + echo -e "\033[1m[INFO]\033[0m Adding necessary packages to the drupal repository." + echo " " + echo >&2 + + cd "$current_folder/codebase" + + # For vlucas/phpdotenv we need the load.environment.php script to be available. + if [[ ! -f ./load.environment.php ]]; then + cp "$current_folder/config/drupal/load.environment.php" . + # Update the composer.json to include it. + local pattern=" \"extra\"" + if $is_darwin; then + local snippet=$'\ "autoload": {"files": ["load.environment.php"]},\n' + sed -i '' -e '\|^'"$pattern"'|i\'$'\n'"$snippet" composer.json + else + local snippet="\ \"autoload\": {\n \"files\": [\"load\.environment\.php\"]\n },\n" + sed -i -e "/^${pattern}/i ${snippet}" composer.json + fi fi + + for i in "${!packages[@]}"; do + local package=${packages[$i]} + local version=${versions[$i]} + # Only installing a package when it is not available in composer.json + if [[ ! $(grep "${package}" composer.json) ]]; then + $composer require ${package}:${version} $composer_general_flags + fi + done + + # Flag that we shouldn't run composer install to initialize everything. + composer_install_run="false" + cd "$current_folder" } -function setup_settings_isle_php() { - local default_dir="$PWD/codebase/web/sites/default" +function create_required_files() { + local drupal_root="$current_folder/codebase/web" + local default_dir="$drupal_root/sites/default" local settings="${default_dir}/settings.php" local settings_default="${default_dir}/default.settings.php" local settings_isle="settings.isle.php" local insert_after="\$settings\[\'entity_update_backup\'\] \= TRUE\;" + local config_sync_pattern="\$settings\['config_sync_directory'] = '\/directory\/outside\/webroot'" local snippet="${config_dir}/snippet.txt" - if [[ -f "${default_dir}/${settings_isle}" ]]; then - fail "An existing settings.isle.php was found under ${default_dir}." + # Prepare the settings file for installation. In case the user has an existing + # one we skip overriding it. + if [[ ! -f "${settings}" && -f "${settings_default}" ]]; then + cp ${settings_default} ${settings} + fi + + # Ensuring that settings.php is pointing to the proper config_sync_directory. + if [[ $(grep "^#.* ${config_sync_pattern}" ${settings} || true) ]]; then + local replace="\$settings\['config_sync_directory'] = '..\/config\/sync'" + if $is_darwin; then + sed -i '' -e "s/^#.* ${config_sync_pattern}/${replace}/" ${settings} + else + sed -i -e "s/^#.* ${config_sync_pattern}/${replace}/" ${settings} + fi fi echo -e "\033[1m[INFO]\033[0m Setting up settings.isle.php to be included in settings.php" echo " " - if [[ ! -f "${settings}" ]]; then - echo " An existing settings.php file couldn't be located and one is created from default.settings.php." - cp ${settings_default} ${settings} - else - echo " An existing settings.php file was found." - fi - sed -i '' -e "/${insert_after}/r ${snippet}" ${settings} - cp "${config_dir}/${settings_isle}" "${default_dir}/${settings_isle}" - echo " settings.isle.php was successfully setup ." - echo >&2 + # Insert settings.isle.php snippet into the settings.php file + if [[ ! -f "${default_dir}/${settings_isle}" ]]; then + if $is_darwin; then + sed -i '' -e "/${insert_after}/r ${snippet}" ${settings} + else + sed -i '' -e "/${insert_after}/r ${snippet}" ${settings} + fi + cp "${config_dir}/${settings_isle}" "${default_dir}/${settings_isle}" + echo " settings.isle.php was successfully setup." + echo >&2 + fi } while [ ! $# -eq 0 ] @@ -102,13 +155,45 @@ do shift done -# Checking if the codebase directory exists and quit. -if [[ -d "$PWD/codebase" && "$(ls -A $PWD/codebase)" ]]; then - fail "The codebase directory exists and is not empty. Please delete it and run this command again." +### +# Determine how we will be running composer. +### +composer=$(command -v composer) +if [[ ! $composer ]]; then + # We use the docker composer image to run composer related commands. + echo >&2 + echo -e "\033[1m[INFO]\033[0m Using the official composer docker image to run composer commands" + echo " " + echo >&2 + composer="mkdir -p $HOME/.composer && docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" +fi + +### +# Checking if the project code exists. +### +if [[ ! -f "$PWD/codebase/composer.json" ]]; then + download_drupal $codebase +else + download_required_packages fi +### # Initialize drupal database and files persistent storage folders. +### mkdir -p $PWD/data/drupal/files/public $PWD/data/drupal/files/private $PWD/data/drupal/database -install_drupal $codebase -setup_settings_isle_php +### +# Running composer install just in case the user has an existing project. +### +if [[ "$composer_install_run" == "true" ]]; then + cd "$current_folder/codebase" + $composer install $composer_flags + cd .. +fi + +### +# Create required files if this init was called for an existing project/codebase. +### +if [[ ! -f "$current_folder/codebase/web/sites/default/settings.isle.php" ]]; then + create_required_files +fi From 7fc6fca052060cccb6b043eafe4ea34442af4a10 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 15:35:18 -0400 Subject: [PATCH 057/107] Added default drupal hash salt variable and pinned to an exact version for the base image. --- docker-compose.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index d3e60d4a6..d7d31a8b5 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,4 @@ -version: '2.4' +version: "2.4" ## ISLE 8 Draft Prototype ## Feb, 2020 @@ -28,7 +28,7 @@ services: - code_dir=./codebase - templates_dir=./config - app_bin_dir=./scripts/app_bin - - base_image_tag=latest + - base_image_tag=7.2.28-1.17.8-0ceedc1b - build_environment=dev # XDEBUG confd build time args - you can set it to 0 turn it off. - PHP_XDEBUG=${PHP_XDEBUG:-1} @@ -60,6 +60,7 @@ services: DB_PORT: ${DB_PORT:-3306} DB_DRIVER: ${DB_DRIVER:-mysql} DRUSH_OPTIONS_URI: ${DRUPAL_LOCAL_URL:-islandora.localhost} + DRUPAL_HASH_SALT: ${DRUPAL_HASH_SALT:-tfvQNpDFG2CjY9WHGNgFqC3eoMjyg5pZdGMQ74zjmnIoe0bi8F3hUvBWnGFIAM3nnj2iWA} NGINX_LISTEN_PORT: "8080" AUTO_INSTALL: "true" volumes: @@ -202,7 +203,7 @@ services: image: traefik:2.1.3 command: --api.insecure=true --providers.docker ports: - - '8000:80' + - "8000:80" #- "443:443" #- "8080:8080" volumes: @@ -212,7 +213,6 @@ services: # - ./config/traefik/acme.json:/acme.json # SSL Choice 2: To use commercial SSLs - uncomment ONLY the line below. Add your SSL certs (.cert, .pem, .key) files to config/traefik/ssl-certs # - ./config/traefik/ssl-certs:/certs:ro - # Use Environment variables to pass in Traefik config; no traefik.yml required # by providers # Alternative to a static configureation /etc/traefik/traefik.yml" From 880fcf46251b06a27edabba36405eae827939d92 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 15:36:09 -0400 Subject: [PATCH 058/107] Made sure that drupal.Dockerfile copy the templates from the base image. --- drupal.Dockerfile | 77 ++++++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/drupal.Dockerfile b/drupal.Dockerfile index 980dd28d6..b998134bd 100644 --- a/drupal.Dockerfile +++ b/drupal.Dockerfile @@ -1,6 +1,6 @@ ARG build_environment=prod ARG code_dir=./codebase -ARG base_image_tag=latest +ARG base_image_tag=7.2.28-1.17.8-0ceedc1b ARG composer_version=1.9.3 ARG templates_dir=./config @@ -40,20 +40,19 @@ COPY ${code_dir}/composer.json ${code_dir}/composer.lock ./ RUN set -eux; \ flags="${COMPOSER_INSTALL_FLAGS}"; \ if [ "$build_environment" == "prod" ]; then \ - flags="${COMPOSER_INSTALL_FLAGS} --no-dev"; \ + flags="${COMPOSER_INSTALL_FLAGS} --no-dev"; \ fi; \ composer install $flags \ # make dummy directory just in case no drupal contrib related dependencies was created. && for dir in $DRUPAL_COMPOSER_DIRECTORIES; do \ - if [ ! -d $dir ]; then \ - mkdir -p $dir; \ - fi; \ + if [ ! -d $dir ]; then \ + mkdir -p $dir; \ + fi; \ done; # # Stage 2: Any node related dependencies can be build here. e.g. compile scss to css # -# Example coming soon # # Stage 3: The base app/drupal @@ -82,42 +81,44 @@ ENV PATH=${PATH}:${APP_ROOT}/vendor/bin \ APP_RUNNER_GROUP=${app_runner_group} \ APP_RUNNER_GROUP_ID=${app_runner_group_id:-1000} -# Copy custom excutable scripts -COPY ${app_bin_dir} /usr/local/bin/ - -# Copy custom configuration files for PHP and NGINX -COPY ${templates_dir}/php/conf.d /etc/confd/conf.d -COPY ${templates_dir}/nginx/conf.d /etc/confd/conf.d -COPY ${templates_dir}/php/templates /etc/confd/templates -COPY ${templates_dir}/nginx/templates /etc/confd/templates +RUN ls -all /confd_templates/; \ + ls -all /drupal/; \ + ls -all /drupal/confd/ -# Make sure docker-webserver-entrypoint and other scripts are executable -RUN chmod -R +x /usr/local/bin/; \ +# Copy custom configuration template files for PHP and NGINX +RUN mkdir -p /etc/confd && cp -R /confd_templates/* /etc/confd/; \ + # Copy custom excutable scripts for drupal including the default entrypoint. + mv /drupal/bin/* /usr/local/bin/; \ + # Make sure docker-webserver-entrypoint and other scripts are executable + chmod -R +x /usr/local/bin/; \ # apply custom configurations based on confd templates /usr/local/bin/confd -onetime -backend env \ # clean the content of confd so that the app can add it's templates later in the process - && rm -rf /etc/confd/* + && rm -rf /etc/confd/* \ + # Move the .env template file for the drupal app. We then run confd in the docker + # entrypoint to place it under /.env. + && cp -R /drupal/confd/* /etc/confd/ && rm -rf /drupal/confd # Add and configure app runner user RUN set -xe; \ # Delete existing user/group if uid/gid occupied. existing_group=$(getent group "${APP_RUNNER_GROUP_ID}" | cut -d: -f1); \ if [ -n "${existing_group}" ]; then delgroup "${existing_group}"; fi; \ - existing_user=$(getent passwd "${APP_RUNNER_USER_ID}" | cut -d: -f1); \ + existing_user=$(getent passwd "${APP_RUNNER_USER_ID}" | cut -d: -f1); \ if [ -n "${existing_user}" ]; then deluser "${existing_user}"; fi; \ \ # Ensure app runner user/group exists - addgroup --system --gid ${APP_RUNNER_GROUP_ID} ${APP_RUNNER_GROUP}; \ - adduser --system --disabled-password --ingroup ${APP_RUNNER_GROUP} --shell /bin/bash --uid ${APP_RUNNER_USER_ID} ${APP_RUNNER_USER}; \ - usermod --append --groups ${NGINX_USER_GROUP} ${APP_RUNNER_USER} \ + addgroup --system --gid ${APP_RUNNER_GROUP_ID} ${APP_RUNNER_GROUP}; \ + adduser --system --disabled-password --ingroup ${APP_RUNNER_GROUP} --shell /bin/bash --uid ${APP_RUNNER_USER_ID} ${APP_RUNNER_USER}; \ + usermod --append --groups ${NGINX_USER_GROUP} ${APP_RUNNER_USER} \ # Other app runner user related configurations. See bin/config_app_runner_user && config_app_runner_user \ \ # Make sure that files dir have proper permissions. && mkdir -p ${FILES_DIR}/public; \ - mkdir -p ${FILES_DIR}/private; \ - # Ensure the files dir is owned by nginx user - chown -R ${NGINX_USER}:${NGINX_USER_GROUP} ${FILES_DIR} + mkdir -p ${FILES_DIR}/private; \ + # Ensure the files dir is owned by nginx user + chown -R ${NGINX_USER}:${NGINX_USER_GROUP} ${FILES_DIR} EXPOSE $NGINX_LISTEN_PORT ENTRYPOINT [ "docker-webserver-entrypoint" ] @@ -137,10 +138,7 @@ COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/w COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/web/profiles/contrib ./web/profiles/contrib COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/web/libraries ./web/libraries COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/drush/contrib ./drush/contrib -# If stage 2 available genrated front end css and css artifacts files can be copied - -# Copy custom configuration files for the app/drupal. We run confd in the docker entrypoint for this. -COPY ${templates_dir}/drupal/confd /etc/confd +# If stage 2 available and generated js and css artifacts files, they can also be copied inside this folder. # # Stage 4: The production setup @@ -149,7 +147,9 @@ FROM base AS prod ENV APP_ENV=prod # Using the production php.ini -RUN mv ${PHP_INI_DIR}/php.ini-production ${PHP_INI_DIR}/php.ini +RUN mv ${PHP_INI_DIR}/php.ini-production ${PHP_INI_DIR}/php.ini; \ + # Remove the confd templates altogether. + rm -rf /confd_templates USER ${APP_RUNNER_USER} @@ -172,15 +172,16 @@ ENV APP_ENV=dev \ RUN pecl install xdebug-2.7.1; \ docker-php-ext-enable xdebug; \ # Adding the dev php.ini - mv ${PHP_INI_DIR}/php.ini-development ${PHP_INI_DIR}/php.ini - -COPY ${templates_dir}/php/conf.d/docker-php-ext-xdebug.ini.toml /etc/confd/conf.d/docker-php-ext-xdebug.ini.toml -COPY ${templates_dir}/php/templates/docker-php-ext-xdebug.ini.tmpl /etc/confd/templates/docker-php-ext-xdebug.ini.tmpl - -# Apply xdebug configurations -RUN /usr/local/bin/confd -onetime -backend env \ - # Delete xdebug configuration template files - && rm /etc/confd/conf.d/docker-php-ext-xdebug.ini.toml /etc/confd/templates/docker-php-ext-xdebug.ini.tmpl + mv ${PHP_INI_DIR}/php.ini-development ${PHP_INI_DIR}/php.ini; \ + # Copy xdebug configurations templates. + cp /confd_templates/conf.d/docker-php-ext-xdebug.ini.toml /etc/confd/conf.d/docker-php-ext-xdebug.ini.toml; \ + cp /confd_templates/templates/docker-php-ext-xdebug.ini.tmpl /etc/confd/templates/docker-php-ext-xdebug.ini.tmpl; \ + # Apply xdebug configurations. + /usr/local/bin/confd -onetime -backend env \ + # Delete xdebug configuration template files. + && rm /etc/confd/conf.d/docker-php-ext-xdebug.ini.toml /etc/confd/templates/docker-php-ext-xdebug.ini.tmpl \ + # Remove the confd templates altogether. + && rm -rf /confd_templates # Copy composer binary from official Composer image. Notice we didn't need composer for prod stage. # @TODO try to use composer_version here From dd526ac98579b6101856a7c900b3e1a68bae3928 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 15:36:53 -0400 Subject: [PATCH 059/107] Removed reference to the php and nginx base image. --- images/nginx-php/Dockerfile | 168 ------------------------------ images/nginx-php/supervisord.conf | 43 -------- 2 files changed, 211 deletions(-) delete mode 100644 images/nginx-php/Dockerfile delete mode 100644 images/nginx-php/supervisord.conf diff --git a/images/nginx-php/Dockerfile b/images/nginx-php/Dockerfile deleted file mode 100644 index f473d958d..000000000 --- a/images/nginx-php/Dockerfile +++ /dev/null @@ -1,168 +0,0 @@ -ARG php_version=7.2.27 -ARG php_pkg_release=fpm-buster - -FROM php:${php_version}-${php_pkg_release} - -ARG nginx_version=1.17.8 -ARG nginx_njs_version=0.3.8 -ARG nginx_pkg_release=1~buster - -ENV CONFD_VERSION=0.16.0 \ - CONFD_SHA256SUM="255d2559f3824dd64df059bdc533fd6b697c070db603c76aaf8d1d5e6b0cc334" \ - FILES_DIR="/mnt/files" \ - APP_ROOT="/var/www/app" \ - APP_DOCROOT="/var/www/app/web" \ - APP_RUNNER_USER=root - -# Install apt dependencies -# @TODO use specific versions of some of these to avoid version conflict or breaking -RUN apt-get update && apt-get install --no-install-recommends --no-install-suggests -y \ - # utilitities cmd - dos2unix rsync wget findutils \ - # for imap - libc-client2007e-dev libkrb5-dev \ - # for ssh client only - openssh-client \ - # for git - git \ - # for bz2 - bzip2 libbz2-dev \ - # for gd - # libfreetype6 libfreetype6-dev libpng-tools libjpeg-dev libgmp-dev libwebp-dev \ - libfreetype6 libfreetype6-dev libpng-tools libgmp-dev libwebp-dev \ - # For image optimization - jpegoptim optipng pngquant \ - # For imagick - imagemagick libmagickwand-dev \ - # For nginx cgi-fcgi - libfcgi0ldbl \ - # for intl - libicu-dev \ - # for mcrypt - libmcrypt-dev \ - # for ldap - libldap2-dev \ - # for zip - libzip-dev zip unzip \ - # for xslt - libxslt1-dev \ - # for postgres - libpq-dev \ - # for tidy - libtidy-dev \ - # for yaml - libyaml-dev \ - # for command like drush sqlc/sqlq which need a mysql client - mariadb-client \ - # for supervsisor (http://supervisord.org/) - supervisor; \ - # install confd - wget -O /usr/local/bin/confd "https://github.com/kelseyhightower/confd/releases/download/v${CONFD_VERSION}/confd-${CONFD_VERSION}-linux-amd64"; \ - chmod +x /usr/local/bin/confd; \ - # verify confd signature - sha256sum /usr/local/bin/confd | grep -q "${CONFD_SHA256SUM}"; \ - exit $?; \ - rm -r /var/lib/apt/lists/* - -# Install and enable php extensions using the helper script provided by the base -# image -RUN docker-php-ext-configure imap --with-kerberos --with-imap-ssl \ - && docker-php-ext-configure gd --with-gd --with-webp-dir \ - --with-freetype-dir=/usr/include/freetype2 \ - --with-jpeg-dir=/usr/include/ \ - --with-png-dir=/usr/include/ \ - && docker-php-ext-configure intl --enable-intl \ - && docker-php-ext-configure ldap --with-libdir=lib/x86_64-linux-gnu \ - && docker-php-ext-configure zip --with-libzip \ - && docker-php-ext-install -j$(nproc) \ - bcmath \ - bz2 \ - calendar \ - exif \ - gd \ - gettext \ - gmp \ - imap \ - intl \ - ldap \ - mysqli \ - opcache \ - pcntl \ - pdo_mysql \ - pdo_pgsql \ - pgsql \ - soap \ - sockets \ - tidy \ - xmlrpc \ - xsl \ - zip \ - # pecl related extensions - && pecl install \ - apcu-5.1.17 \ - imagick-3.4.4 \ - mcrypt-1.0.1 \ - yaml-2.0.4 \ - && docker-php-ext-enable \ - apcu \ - imagick \ - mcrypt \ - yaml - -# install nginx (copied from official nginx Dockerfile https://github.com/nginxinc/docker-nginx/blob/c817e28dd68b6daa33265a8cb527b1c4cd723b59/mainline/buster/Dockerfile) -ENV NGINX_VERSION=${nginx_version} \ - NGINX_PKG_RELEASE=${nginx_pkg_release} \ - NJS_VERSION=${nginx_njs_version} \ - NGINX_USER=www-data \ - NGINX_USER_GROUP=www-data - -RUN set -x \ - # Ensure nginx user/group first, to be consistent throughout docker variants - && user_exists=$(id -u ${NGINX_USER} > /dev/null 2>&1; echo $?) \ - && if [ user_exists -eq 0 ]; then \ - addgroup --system --gid 101 ${NGINX_USER_GROUP}; \ - adduser --system --disabled-login \ - --ingroup ${NGINX_USER_GROUP} --no-create-home \ - --home /nonexistent --gecos "${NGINX_USER} user" \ - --shell /bin/false --uid 101 ${NGINX_USER}; \ - fi \ - && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y gnupg1 \ - && \ - NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \ - found=''; \ - for server in \ - ha.pool.sks-keyservers.net \ - hkp://keyserver.ubuntu.com:80 \ - hkp://p80.pool.sks-keyservers.net:80 \ - pgp.mit.edu \ - ; do \ - echo "Fetching GPG key $NGINX_GPGKEY from $server"; \ - apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \ - done; \ - test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \ - apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \ - && nginxPackages=" \ - nginx=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ - nginx-module-xslt=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ - nginx-module-geoip=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ - nginx-module-image-filter=${NGINX_VERSION}-${NGINX_PKG_RELEASE} \ - nginx-module-njs=${NGINX_VERSION}.${NJS_VERSION}-${NGINX_PKG_RELEASE} \ - " \ - # Arches officialy built by upstream - && echo "deb https://nginx.org/packages/mainline/debian/ buster nginx" >> /etc/apt/sources.list.d/nginx.list \ - && apt-get update \ - && apt-get install --no-install-recommends --no-install-suggests -y \ - $nginxPackages \ - gettext-base \ - && apt-get remove --purge --auto-remove -y \ - && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \ - # Create default place to store files generated by the app - && install -o ${NGINX_USER} -g ${NGINX_USER} -d "${FILES_DIR}/public" "${FILES_DIR}/private"; \ - chmod -R 775 "${FILES_DIR}" - -EXPOSE 80 443 - -# Copy custom supervisord configuration file -COPY ./supervisord.conf /etc/supervisor/conf.d/supervisord.conf -CMD ["/usr/bin/supervisord", "-n", "-c", "/etc/supervisor/conf.d/supervisord.conf"] diff --git a/images/nginx-php/supervisord.conf b/images/nginx-php/supervisord.conf deleted file mode 100644 index 2062b3792..000000000 --- a/images/nginx-php/supervisord.conf +++ /dev/null @@ -1,43 +0,0 @@ -[supervisord] -user=%(ENV_APP_RUNNER_USER)s -nodaemon=true -logfile=/dev/null -logfile_maxbytes=0 -pidfile=/var/run/supervisord.pid -loglevel = INFO - -[unix_http_server] -file=/var/run/supervisor.sock -chmod=0700 -username=docker -password=docker - -[supervisorctl] -serverurl=unix:///var/run/supervisord.sock -username=docker -password=docker - -[rpcinterface:supervisor] -supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface - -[program:php-fpm] -command = /usr/local/sbin/php-fpm -autostart=true -autorestart=true -priority=5 -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 - -[program:nginx] -command=/usr/sbin/nginx -g "daemon off;" -autostart=true -autorestart=true -priority=10 -stdout_events_enabled=true -stderr_events_enabled=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 From 5c77e981fbf2b8f11a5aff95bf96d9b9314f4ec8 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 16:04:47 -0400 Subject: [PATCH 060/107] Adding print message when downloading required packages. --- scripts/drupal/init.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 96a818911..43ad70f38 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -83,9 +83,14 @@ function download_required_packages() { local version=${versions[$i]} # Only installing a package when it is not available in composer.json if [[ ! $(grep "${package}" composer.json) ]]; then + echo " Requiring ${package}. Skipping." $composer require ${package}:${version} $composer_general_flags + else + echo " ${package} was found in the composer.json. Skipping." fi done + echo " " + echo >&2 # Flag that we shouldn't run composer install to initialize everything. composer_install_run="false" From 26e7bf5c41b395cdde9b20d54cfd761e3551537b Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 16:23:08 -0400 Subject: [PATCH 061/107] The drupal.Dockerfile support by default the islandora codebase. --- drupal.Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drupal.Dockerfile b/drupal.Dockerfile index b998134bd..cd3767617 100644 --- a/drupal.Dockerfile +++ b/drupal.Dockerfile @@ -36,6 +36,9 @@ RUN chmod 0600 /root/.ssh \ WORKDIR /app COPY ${code_dir}/composer.json ${code_dir}/composer.lock ./ +# This only work when the codebase is islandora. It should probably be commented out if installing +# the drupal/recommended-project. +COPY ${code_dir}/scripts/composer/ScriptHandler.php ./scripts/composer/ScriptHandler.php RUN set -eux; \ flags="${COMPOSER_INSTALL_FLAGS}"; \ From 5c03d7b1d32a03e6279ecf48711f5214de143f1a Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 17:05:37 -0400 Subject: [PATCH 062/107] Initial workflow implementation. --- .github/docker-compose.override.yml | 4 ++++ .github/workflows/ci.yml | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 .github/docker-compose.override.yml create mode 100644 .github/workflows/ci.yml diff --git a/.github/docker-compose.override.yml b/.github/docker-compose.override.yml new file mode 100644 index 000000000..31a46bd10 --- /dev/null +++ b/.github/docker-compose.override.yml @@ -0,0 +1,4 @@ +version: "2.4" + +volumes: + mariadb-data: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..a1998028d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,20 @@ +--- +name: CI +"on": + push: + branches: + - master + - development + pull_request: + +jobs: + test: + name: Test the main docker-compose.yml default usage. + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + + steps: + - name: Checking docker version + run: docker --version From cfd6d2150a6be6234e8990c02a08a61e512e127b Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 17:39:49 -0400 Subject: [PATCH 063/107] Checking docker and compose version and also checking out the code.. --- .github/workflows/ci.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a1998028d..ab2fb8a0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,8 +13,11 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest] + os: [ubuntu-latest] steps: + - name: Check out code + uses: actions/checkout@v2 + - name: Checking docker version - run: docker --version + run: docker --version && docker-compose --version From 5c33860a5516eefe3319391e45673bba98f7344d Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 18:41:00 -0400 Subject: [PATCH 064/107] Added more steps to check the home page. --- .github/docker-compose.override.yml | 8 ++++++++ .github/workflows/ci.yml | 19 +++++++++++++++++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/.github/docker-compose.override.yml b/.github/docker-compose.override.yml index 31a46bd10..66e263388 100644 --- a/.github/docker-compose.override.yml +++ b/.github/docker-compose.override.yml @@ -1,4 +1,12 @@ version: "2.4" +services: + drupal: + ports: + - "80:8080" + # Removing traefik functionality this way. https://github.com/docker/compose/issues/3729 + traefik: + image: hello-world + volumes: mariadb-data: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ab2fb8a0f..997fad370 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,8 +16,23 @@ jobs: os: [ubuntu-latest] steps: - - name: Check out code + - name: Check out code. uses: actions/checkout@v2 - - name: Checking docker version + - name: Checking docker version. run: docker --version && docker-compose --version + + - name: Configure and run docker-compose with make. + run: | + mv .github/docker-compose.override.yml docker-compose.override.yml + make + + - name: Wait for Drupal to install. + run: | + while ! docker container logs islandora_drupal_1 | grep "nginx entered RUNNING state"; do + sleep 1 + done + + - name: Test loading the home page. + run: > + curl -s http://localhost | grep "

Welcome to islandora

" From c06549174c670cc1f6a009dc5f7ea7c082a59a4d Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 18:45:38 -0400 Subject: [PATCH 065/107] Fixing sed for isle settings on other distribution. --- scripts/drupal/init.sh | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 43ad70f38..e2649a12f 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -123,15 +123,14 @@ function create_required_files() { fi fi - echo -e "\033[1m[INFO]\033[0m Setting up settings.isle.php to be included in settings.php" - echo " " - # Insert settings.isle.php snippet into the settings.php file if [[ ! -f "${default_dir}/${settings_isle}" ]]; then + echo -e "\033[1m[INFO]\033[0m Setting up settings.isle.php to be included in settings.php" + echo " " if $is_darwin; then sed -i '' -e "/${insert_after}/r ${snippet}" ${settings} else - sed -i '' -e "/${insert_after}/r ${snippet}" ${settings} + sed -i -e "/${insert_after}/r ${snippet}" ${settings} fi cp "${config_dir}/${settings_isle}" "${default_dir}/${settings_isle}" echo " settings.isle.php was successfully setup." From 03442fe9c8b171f00f4d13fcb8a74086cbf86e63 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 18:58:01 -0400 Subject: [PATCH 066/107] Running some of the commands step by step. --- .github/workflows/ci.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 997fad370..3a7faac4c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,10 +22,11 @@ jobs: - name: Checking docker version. run: docker --version && docker-compose --version - - name: Configure and run docker-compose with make. - run: | - mv .github/docker-compose.override.yml docker-compose.override.yml - make + - name: Initialize the codebase. + run: ./scripts/drupal/init.sh --codebase islandora + + - name: Run docker-compose up. + run: docker-compose -p islandora up --remove-orphans --detach - name: Wait for Drupal to install. run: | From 119eb5c0f034a27763343aa805e74e3ba7ab0321 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 19:14:42 -0400 Subject: [PATCH 067/107] Trying to build the image only to see why it's failing with docker compose. --- .github/.dockerignore | 14 ++++++++++++++ .github/workflows/ci.yml | 40 +++++++++++++++++++++++++++++----------- 2 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 .github/.dockerignore diff --git a/.github/.dockerignore b/.github/.dockerignore new file mode 100644 index 000000000..8b97d98d9 --- /dev/null +++ b/.github/.dockerignore @@ -0,0 +1,14 @@ +.git +.idea +.gitlab +.vscode +.gitlab-ci.yml +*/mysql +/mysql +*/files +/files +vendor/ +node_modules/ +*.sql +*.tmp +/data diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3a7faac4c..56048128a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,17 +23,35 @@ jobs: run: docker --version && docker-compose --version - name: Initialize the codebase. - run: ./scripts/drupal/init.sh --codebase islandora - - - name: Run docker-compose up. - run: docker-compose -p islandora up --remove-orphans --detach - - - name: Wait for Drupal to install. run: | - while ! docker container logs islandora_drupal_1 | grep "nginx entered RUNNING state"; do - sleep 1 - done + mv .github/docker-compose.override.yml docker-compose.override.yml + mv .github/.dockerignore .dockerignore + ./scripts/drupal/init.sh --codebase islandora - - name: Test loading the home page. + - name: Build app image run: > - curl -s http://localhost | grep "

Welcome to islandora

" + docker image build + --pull + --cache-from $IMAGE_NAME_LATEST + --tag $IMAGE_NAME_TAG + --tag $IMAGE_NAME_LATEST + --target dev + --build-arg code_dir=./codebase + --build-arg templates_dir=./config + --build-arg app_bin_dir=./scripts/app_bin + --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b + --build-arg build_environment=dev + --build-arg NGINX_LISTEN_PORT=8080 + --build-arg NGINX_SERVER_ROOT=/var/www/app/web + --file drupal.Dockerfile + . + # - name: Run docker-compose up. + # run: docker-compose -p islandora up --remove-orphans --detach + # - name: Wait for Drupal to install. + # run: | + # while ! docker container logs islandora_drupal_1 | grep "nginx entered RUNNING state"; do + # sleep 1 + # done + # - name: Test loading the home page. + # run: > + # curl -s http://localhost | grep "

Welcome to islandora

" From dd51bf648cc7c8fff3aef68dd26bf4d259127faa Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 19:17:01 -0400 Subject: [PATCH 068/107] Remove unneeded args. --- .github/workflows/ci.yml | 2 -- docker-compose.yml | 2 -- drupal.Dockerfile | 4 ---- 3 files changed, 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 56048128a..913698698 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,8 +37,6 @@ jobs: --tag $IMAGE_NAME_LATEST --target dev --build-arg code_dir=./codebase - --build-arg templates_dir=./config - --build-arg app_bin_dir=./scripts/app_bin --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b --build-arg build_environment=dev --build-arg NGINX_LISTEN_PORT=8080 diff --git a/docker-compose.yml b/docker-compose.yml index d7d31a8b5..09e431930 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -26,8 +26,6 @@ services: target: dev args: - code_dir=./codebase - - templates_dir=./config - - app_bin_dir=./scripts/app_bin - base_image_tag=7.2.28-1.17.8-0ceedc1b - build_environment=dev # XDEBUG confd build time args - you can set it to 0 turn it off. diff --git a/drupal.Dockerfile b/drupal.Dockerfile index cd3767617..5b572df1a 100644 --- a/drupal.Dockerfile +++ b/drupal.Dockerfile @@ -2,7 +2,6 @@ ARG build_environment=prod ARG code_dir=./codebase ARG base_image_tag=7.2.28-1.17.8-0ceedc1b ARG composer_version=1.9.3 -ARG templates_dir=./config # # Stage 1: PHP Dependencies @@ -62,12 +61,10 @@ RUN set -eux; \ # FROM registry.gitlab.com/nikathone/drupal-docker-good-defaults/php-nginx:${base_image_tag} as base ARG code_dir -ARG templates_dir ARG app_runner_user=drupal ARG app_runner_user_id=1000 ARG app_runner_group=drupal ARG app_runner_group_id=1000 -ARG app_bin_dir=./scripts/app_bin ARG NGINX_LISTEN_PORT=8080 ARG NGINX_SERVER_ROOT @@ -161,7 +158,6 @@ USER ${APP_RUNNER_USER} # FROM base AS dev ARG composer_version -ARG templates_dir ARG PHP_XDEBUG ARG PHP_XDEBUG_DEFAULT_ENABLE ARG PHP_XDEBUG_REMOTE_CONNECT_BACK From e906fd688eb4dbc27c09474cdfb99e08eaaa3015 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 17 Mar 2020 19:37:54 -0400 Subject: [PATCH 069/107] Wrong tag name. --- .github/workflows/ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 913698698..7c3107ee9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,10 +31,7 @@ jobs: - name: Build app image run: > docker image build - --pull - --cache-from $IMAGE_NAME_LATEST - --tag $IMAGE_NAME_TAG - --tag $IMAGE_NAME_LATEST + --tag app-image-test:latest --target dev --build-arg code_dir=./codebase --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b From 62749cede137bdb794102e17510b1edfb4e0c531 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 00:31:43 -0400 Subject: [PATCH 070/107] Adding gitlab-ci for testing. --- .gitlab-ci.yml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..619b3da90 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,25 @@ +variables: + DOCKER_VERSION: "19.03" + +test: + image: registry.gitlab.com/nikathone/image-utils/docker:$DOCKER_VERSION-compose + stage: test + services: + - registry.gitlab.com/nikathone/image-utils/docker:$DOCKER_VERSION-dind + before_script: + - docker --version && docker-compose --version + script: + - | + mv .github/docker-compose.override.yml docker-compose.override.yml + mv .github/.dockerignore .dockerignore + ./scripts/drupal/init.sh --codebase islandora + - > + docker image build + --tag app-image-test:latest + --target dev + --build-arg code_dir=./codebase + --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b + --build-arg build_environment=dev + --build-arg NGINX_LISTEN_PORT=8080 + --build-arg NGINX_SERVER_ROOT=/var/www/app/web + --file drupal.Dockerfile From 5e94865f23078c1d020f8c19579625e510ea03fc Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 00:43:19 -0400 Subject: [PATCH 071/107] Trying with official docker dind. --- .gitlab-ci.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 619b3da90..db2f8cbbf 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,11 +1,14 @@ variables: - DOCKER_VERSION: "19.03" + DOCKER_VERSION: "19.03.8" test: image: registry.gitlab.com/nikathone/image-utils/docker:$DOCKER_VERSION-compose stage: test + # variables: + # DOCKER_DRIVER: overlay2 + # DOCKER_TLS_CERTDIR: "/certs" services: - - registry.gitlab.com/nikathone/image-utils/docker:$DOCKER_VERSION-dind + - docker:$DOCKER_VERSION-dind before_script: - docker --version && docker-compose --version script: @@ -23,3 +26,7 @@ test: --build-arg NGINX_LISTEN_PORT=8080 --build-arg NGINX_SERVER_ROOT=/var/www/app/web --file drupal.Dockerfile + only: + refs: + - master + - development From 7bcf43d295eaeaf7eacb3cdac3396e4d4653c157 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 00:47:03 -0400 Subject: [PATCH 072/107] Remove multiline. --- .gitlab-ci.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index db2f8cbbf..b405f53af 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,10 +12,9 @@ test: before_script: - docker --version && docker-compose --version script: - - | - mv .github/docker-compose.override.yml docker-compose.override.yml - mv .github/.dockerignore .dockerignore - ./scripts/drupal/init.sh --codebase islandora + - mv .github/docker-compose.override.yml docker-compose.override.yml + - mv .github/.dockerignore .dockerignore + - ./scripts/drupal/init.sh --codebase islandora - > docker image build --tag app-image-test:latest From f95a3e18fef1c03e30ceb4c82dd9607d31d23afb Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 01:01:12 -0400 Subject: [PATCH 073/107] Only running mov commands. --- .gitlab-ci.yml | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b405f53af..ec93cf9ce 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,17 +14,17 @@ test: script: - mv .github/docker-compose.override.yml docker-compose.override.yml - mv .github/.dockerignore .dockerignore - - ./scripts/drupal/init.sh --codebase islandora - - > - docker image build - --tag app-image-test:latest - --target dev - --build-arg code_dir=./codebase - --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b - --build-arg build_environment=dev - --build-arg NGINX_LISTEN_PORT=8080 - --build-arg NGINX_SERVER_ROOT=/var/www/app/web - --file drupal.Dockerfile + # - ./scripts/drupal/init.sh --codebase islandora + # - > + # docker image build + # --tag app-image-test:latest + # --target dev + # --build-arg code_dir=./codebase + # --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b + # --build-arg build_environment=dev + # --build-arg NGINX_LISTEN_PORT=8080 + # --build-arg NGINX_SERVER_ROOT=/var/www/app/web + # --file drupal.Dockerfile only: refs: - master From 5ad6e760f5fb948b41674150d7a50e5e6a7ab3f1 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 11:11:49 -0400 Subject: [PATCH 074/107] Making sure init.sh is executable. --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ec93cf9ce..356c16276 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,6 +14,7 @@ test: script: - mv .github/docker-compose.override.yml docker-compose.override.yml - mv .github/.dockerignore .dockerignore + - chmod +x scripts/drupal/init.sh # - ./scripts/drupal/init.sh --codebase islandora # - > # docker image build From e3de10197b2ba3c7b9f63e4d08085a8df41cb420 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 11:35:07 -0400 Subject: [PATCH 075/107] Making sure I am delaing with ubuntu. --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 356c16276..826c8761b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,6 +15,8 @@ test: - mv .github/docker-compose.override.yml docker-compose.override.yml - mv .github/.dockerignore .dockerignore - chmod +x scripts/drupal/init.sh + - echo "$(uname -s)" + - if [[ -f /etc/os-release ]]; then cat /etc/os-release; fi # - ./scripts/drupal/init.sh --codebase islandora # - > # docker image build From 1203f30d76258e3af5e7f906c939a9e557611450 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 12:14:10 -0400 Subject: [PATCH 076/107] Checking files permissions in Gitlab CI. --- .gitlab-ci.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 826c8761b..02308ad0e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -14,9 +14,8 @@ test: script: - mv .github/docker-compose.override.yml docker-compose.override.yml - mv .github/.dockerignore .dockerignore + - ls -all scripts/drupal/ - chmod +x scripts/drupal/init.sh - - echo "$(uname -s)" - - if [[ -f /etc/os-release ]]; then cat /etc/os-release; fi # - ./scripts/drupal/init.sh --codebase islandora # - > # docker image build @@ -28,6 +27,9 @@ test: # --build-arg NGINX_LISTEN_PORT=8080 # --build-arg NGINX_SERVER_ROOT=/var/www/app/web # --file drupal.Dockerfile + after_script: + - echo "$(uname -s)" + - if [[ -f /etc/os-release ]]; then cat /etc/os-release; fi only: refs: - master From f379f2ca84bf3a699aa1b0d312886a0cd5d31039 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 12:23:24 -0400 Subject: [PATCH 077/107] Making sure that checking composer won't break the CI run in gitlab. --- .gitlab-ci.yml | 2 +- scripts/drupal/init.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 02308ad0e..8610dc027 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -16,7 +16,7 @@ test: - mv .github/.dockerignore .dockerignore - ls -all scripts/drupal/ - chmod +x scripts/drupal/init.sh - # - ./scripts/drupal/init.sh --codebase islandora + - scripts/drupal/init.sh --codebase islandora # - > # docker image build # --tag app-image-test:latest diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index e2649a12f..45cd24eda 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -162,7 +162,7 @@ done ### # Determine how we will be running composer. ### -composer=$(command -v composer) +composer=$(command -v composer || true) if [[ ! $composer ]]; then # We use the docker composer image to run composer related commands. echo >&2 From 22dd00be5fd67eabcde5d7475fdb915e8f65a34e Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 12:32:24 -0400 Subject: [PATCH 078/107] Replaced the HOME environment variable with tilda. --- .gitlab-ci.yml | 1 + scripts/drupal/init.sh | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8610dc027..a70a2436d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,6 +15,7 @@ test: - mv .github/docker-compose.override.yml docker-compose.override.yml - mv .github/.dockerignore .dockerignore - ls -all scripts/drupal/ + - echo "$HOME" - chmod +x scripts/drupal/init.sh - scripts/drupal/init.sh --codebase islandora # - > diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 45cd24eda..a1480b474 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -169,7 +169,8 @@ if [[ ! $composer ]]; then echo -e "\033[1m[INFO]\033[0m Using the official composer docker image to run composer commands" echo " " echo >&2 - composer="mkdir -p $HOME/.composer && docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" + mkdir -p ~/.composer + composer="docker container run -it --rm -v ~/.composer:/tmp -v $PWD:/app composer:1.9.3" fi ### From 64602cf3590f1c12a7011ab4c642265e826ef3ce Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 13:28:33 -0400 Subject: [PATCH 079/107] Removed the interactivity of the container for none Darwin system. --- scripts/drupal/init.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index a1480b474..461431543 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -170,7 +170,11 @@ if [[ ! $composer ]]; then echo " " echo >&2 mkdir -p ~/.composer - composer="docker container run -it --rm -v ~/.composer:/tmp -v $PWD:/app composer:1.9.3" + if $is_darwin; then + composer="docker container run -it --rm -v ~/.composer:/tmp -v $PWD:/app composer:1.9.3" + else + composer="docker container run -t --rm -v ~/.composer:/tmp -v $PWD:/app composer:1.9.3" + fi fi ### From 5c01761c98f9d76db585806967f9d742951b3551 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 13:35:21 -0400 Subject: [PATCH 080/107] Removed the interactivity of the container for none Darwin system. --- scripts/drupal/init.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 461431543..68dc9f42f 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -169,11 +169,11 @@ if [[ ! $composer ]]; then echo -e "\033[1m[INFO]\033[0m Using the official composer docker image to run composer commands" echo " " echo >&2 - mkdir -p ~/.composer + mkdir -p "$HOME/.composer" if $is_darwin; then - composer="docker container run -it --rm -v ~/.composer:/tmp -v $PWD:/app composer:1.9.3" + composer="docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" else - composer="docker container run -t --rm -v ~/.composer:/tmp -v $PWD:/app composer:1.9.3" + composer="docker container run -t -- rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" fi fi From 3fa97c98d9f60c2316a125d0eab6d645240978e2 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 14:50:21 -0400 Subject: [PATCH 081/107] A typo for the remove flag. --- scripts/drupal/init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 68dc9f42f..268834d74 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -173,7 +173,7 @@ if [[ ! $composer ]]; then if $is_darwin; then composer="docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" else - composer="docker container run -t -- rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" + composer="docker container run -t --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" fi fi From 7a0a5301bc7dcc4cb6abc6fed2fb049fd33d9bd1 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 16:57:06 -0400 Subject: [PATCH 082/107] Running make in the gitlab CI. --- .github/docker-compose.override.yml | 3 ++- .gitlab-ci.yml | 18 +++++++----------- Makefile | 2 -- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/.github/docker-compose.override.yml b/.github/docker-compose.override.yml index 66e263388..3949ae8d8 100644 --- a/.github/docker-compose.override.yml +++ b/.github/docker-compose.override.yml @@ -4,7 +4,8 @@ services: drupal: ports: - "80:8080" - # Removing traefik functionality this way. https://github.com/docker/compose/issues/3729 + # Removing traefik service functionality cause we don't need it during testing. + # https://github.com/docker/compose/issues/3729 traefik: image: hello-world diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a70a2436d..ac1f1b911 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,17 +17,13 @@ test: - ls -all scripts/drupal/ - echo "$HOME" - chmod +x scripts/drupal/init.sh - - scripts/drupal/init.sh --codebase islandora - # - > - # docker image build - # --tag app-image-test:latest - # --target dev - # --build-arg code_dir=./codebase - # --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b - # --build-arg build_environment=dev - # --build-arg NGINX_LISTEN_PORT=8080 - # --build-arg NGINX_SERVER_ROOT=/var/www/app/web - # --file drupal.Dockerfile + - make + - > + while ! docker container logs islandora_drupal_1 | grep "nginx entered RUNNING state"; do + echo "Waiting for the site to be installed!" + sleep 2m + done + - curl -s http://localhost:8000 | grep '

Welcome to islandora

' after_script: - echo "$(uname -s)" - if [[ -f /etc/os-release ]]; then cat /etc/os-release; fi diff --git a/Makefile b/Makefile index b5f532419..c942fc80d 100644 --- a/Makefile +++ b/Makefile @@ -38,8 +38,6 @@ down_rmi_local: --volumes \ --remove-orphans -# @todo we might chmod here since drupal set settings.php to readonly after -# install drupal_clean: chmod u+w codebase/web/sites/default && rm -rf codebase data/drupal From 20e53737290def8da7d8430f959a6a31f2d5121d Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 18 Mar 2020 20:00:49 -0400 Subject: [PATCH 083/107] Using drupal-composer/drupal-project for drupal codebase and try a simple build image in the CI. --- .gitlab-ci.yml | 26 +++++++++++++++++--------- scripts/drupal/init.sh | 3 ++- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ac1f1b911..b1d677521 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -4,9 +4,6 @@ variables: test: image: registry.gitlab.com/nikathone/image-utils/docker:$DOCKER_VERSION-compose stage: test - # variables: - # DOCKER_DRIVER: overlay2 - # DOCKER_TLS_CERTDIR: "/certs" services: - docker:$DOCKER_VERSION-dind before_script: @@ -17,13 +14,24 @@ test: - ls -all scripts/drupal/ - echo "$HOME" - chmod +x scripts/drupal/init.sh - - make - > - while ! docker container logs islandora_drupal_1 | grep "nginx entered RUNNING state"; do - echo "Waiting for the site to be installed!" - sleep 2m - done - - curl -s http://localhost:8000 | grep '

Welcome to islandora

' + docker image build + --tag app-image-test:latest + --target dev + --build-arg code_dir=./codebase + --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b + --build-arg build_environment=dev + --build-arg NGINX_LISTEN_PORT=8080 + --build-arg NGINX_SERVER_ROOT=/var/www/app/web + --file drupal.Dockerfile + . + # - make + # - > + # while ! docker container logs islandora_drupal_1 | grep "nginx entered RUNNING state"; do + # echo "Waiting for the site to be installed!" + # sleep 2m + # done + # - curl -s http://localhost | grep '

Welcome to islandora

' after_script: - echo "$(uname -s)" - if [[ -f /etc/os-release ]]; then cat /etc/os-release; fi diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 268834d74..2980bd45a 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -29,7 +29,8 @@ function help() { } function download_drupal() { - local args="create-project drupal/recommended-project:^8.8" + local args="create-project drupal-composer/drupal-project:8.x-dev" + # local args="create-project drupal/recommended-project:^8.8" local flags="--ignore-platform-reqs --no-interaction" local codebase="$1" local drush_require="require drush/drush $flags" From 12cad8673397697c600a787b1bf68a884a092970 Mon Sep 17 00:00:00 2001 From: "Noah W. Smith" Date: Thu, 19 Mar 2020 17:27:33 -0400 Subject: [PATCH 084/107] adding Solr back in; adding default Islandora config --- Makefile | 5 +++- docker-compose.yml | 56 ++++++++++++++++++------------------- scripts/drupal/init.sh | 11 +++++++- scripts/solr/create-core.sh | 1 + 4 files changed, 43 insertions(+), 30 deletions(-) create mode 100755 scripts/solr/create-core.sh diff --git a/Makefile b/Makefile index c942fc80d..06369ab03 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ docker_compose_project ?= islandora .PHONY: help drupal_init up build down down_rmi_all down_rmi_local drupal_clean clean_local clean -default: drupal_init up +default: drupal_init solr_init up help: ./scripts/drupal/init.sh --help @@ -14,6 +14,9 @@ help: drupal_init: ./scripts/drupal/init.sh --codebase $(isle_codebase) +solr_init: + ./scripts/solr/create-core.sh + up: docker-compose -p $(docker_compose_project) up --remove-orphans --detach diff --git a/docker-compose.yml b/docker-compose.yml index 09e431930..5c25ee1c8 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -77,25 +77,25 @@ services: - "traefik.http.routers.${APP_NAME:-islandora}.rule=Host(`${DRUPAL_LOCAL_URL:-islandora.localhost}`)" - "traefik.http.services.${APP_NAME:-islandora}.loadbalancer.server.port=8080" - # solr: - # image: wodby/solr:$SOLR_TAG - # container_name: "${PROJECT_NAME}_solr" - # networks: - # - internal - # environment: - # SOLR_DEFAULT_CONFIG_SET: $SOLR_CONFIG_SET - # SOLR_HEAP: 1024m - # volumes: - # - solr-data:/opt/solr/server/solr - # # TO DO: Determine what type of container handling is needed - # labels: - # - "traefik.http.routers.${PROJECT_NAME}_solr.rule=Host(`solr.${PROJECT_BASE_URL}`)" + solr: + image: wodby/solr:$SOLR_TAG + container_name: "${PROJECT_NAME}_solr" + # networks: + # - internal + environment: + SOLR_DEFAULT_CONFIG_SET: $SOLR_CONFIG_SET + SOLR_HEAP: 1024m + volumes: + - solr-data:/opt/solr/server/solr + # TO DO: Determine what type of container handling is needed + labels: + - "traefik.http.routers.${PROJECT_NAME}_solr.rule=Host(`solr.${PROJECT_BASE_URL}`)" cantaloupe: image: lyrasis/cantaloupe:latest # should this be 4.0-1 instead? container_name: "${PROJECT_NAME}_cantaloupe" - networks: - - internal + # networks: + # - internal env_file: - cantaloupe.env ports: @@ -119,8 +119,8 @@ services: # HOUDINI_JWT_ADMIN_TOKEN: $HOUDINI_JWT_ADMIN_TOKEN # HOUDINI_LOG_LEVEL: $HOUDINI_LOG_LEVEL container_name: "${PROJECT_NAME}_houdini" - networks: - - internal + # networks: + # - internal # ports: # - "8000:8000" volumes: @@ -155,8 +155,8 @@ services: # HYPERCUBE_JWT_ADMIN_TOKEN: $HYPERCUBE_JWT_ADMIN_TOKEN # HYPERCUBE_LOG_LEVEL: $HYPERCUBE_LOG_LEVEL container_name: "${PROJECT_NAME}_hypercube" - networks: - - internal + # networks: + # - internal # ports: # - "8002:8000" volumes: @@ -172,8 +172,8 @@ services: # args: # FITS_WEBSERVICE_URI: http://fits:8080/fits/examine container_name: "${PROJECT_NAME}_crayfits" - networks: - - internal + # networks: + # - internal # ports: # - "8003:8000" volumes: @@ -184,8 +184,8 @@ services: fits: image: harvardlts/fitsservlet_container:1.5.0 container_name: "${PROJECT_NAME}_fits" - networks: - - internal + # networks: + # - internal # ports: # - '8085:8080' # TO DO: Determine what type of container handling is needed, exposed ports etc? @@ -222,8 +222,8 @@ services: container_name: "${PROJECT_NAME}_alpaca" image: birkland/isle-alpaca@sha256:9fdf260df79b38fd240192ec618e4eec74cca2b6157b4e59f7d4792295cfe210 env_file: .env - networks: - - internal + # networks: + # - internal activemq: image: rmohr/activemq @@ -231,8 +231,8 @@ services: env_file: .env ports: - "8161:8161" # To make mgmt console available for dev purposes. use admin:admin to log in - networks: - - internal + # networks: + # - internal volumes: - activemq-data:/opt/activemq/data @@ -252,5 +252,5 @@ volumes: device: ${PWD}/data/drupal/database solr-data: activemq-data: - # solr-data: + solr-data: # isle-dc-postgres-data # Added to prototype but not currently used diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 2980bd45a..95339e78a 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -40,7 +40,7 @@ function download_drupal() { fi if [[ "$codebase" == "islandora" ]]; then - local args="create-project islandora/drupal-project" + local args="create-project born-digital/drupal-project:dev-isle8-dev" fi echo -e "\033[1m[INFO]\033[0m Installing drupal using composer" @@ -187,6 +187,15 @@ else download_required_packages fi + + +# load the config here +if [[ "$codebase" == "islandora" ]]; then + if [[ ! "$(ls -A codebase/config/sync)" && ! -f codebase/config/sync/core.extension.yml ]]; then + tar -xzf config/drupal/islandora-starter-config.tar.gz codebase/config/sync + fi +fi + ### # Initialize drupal database and files persistent storage folders. ### diff --git a/scripts/solr/create-core.sh b/scripts/solr/create-core.sh new file mode 100755 index 000000000..d0348519e --- /dev/null +++ b/scripts/solr/create-core.sh @@ -0,0 +1 @@ +docker-compose -p islandora exec solr /opt/solr/bin/solr create -c islandora From 87420ce7cd3dfea76500e3199c9aefa76e07ae1f Mon Sep 17 00:00:00 2001 From: "Noah W. Smith" Date: Fri, 20 Mar 2020 13:54:20 -0400 Subject: [PATCH 085/107] force add islandora starter config tar --- config/drupal/islandora-starter-config.tar.gz | Bin 0 -> 133224 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 config/drupal/islandora-starter-config.tar.gz diff --git a/config/drupal/islandora-starter-config.tar.gz b/config/drupal/islandora-starter-config.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..f15c66e680d1398ce8ecaa43b187a32b5c3a22e7 GIT binary patch literal 133224 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

dd4c0`QkrN++OUhnlRSM+C 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

    2dhp7s+Bz@F;&CzFJ8RaVf%Dv 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�tvKnzO zmP4re<1YqsM8=qZaeI26Qg!`X0zgS;fau7!%-F8T^$`aE;tndK(#Mw*3#Nfvc1`Y# zJ(hsm1Z{NA(tX&i?Ge1Rx<+KAAOY7XblZ#}K;!kS6@Bc3?214RneMB<(XH4fvPCID zN4aXvNYo9hmTl5t%X;qHb%7Y!gWK9-$sqbOQ2rG(Y={(6WPYl2lgm`q3Cwm7Dqe8} z{be0Bbrd1Cu!F9QJ-+X1iB;T1QwFWLTC<=g)5Mv5F^EW4)wju3;MLBIBSQq?hPDnK;IAV%pQ265)=8+JqD5y@jT}WX1&S0)n%*so z=D3RzO%5GPr_LVyZ@C7;K62|BumEAUaUcwv197L%!FY3U+o0ojZ4M4|Hk*;|N#hGI zbh=Ztv^`OC_BNYcZ=2liXx`jzC$cg;B2Cdg(z!UEkd7oH(BCdR1S2ytC(D>1ibCSy z05+wJN!st@@oP%rlLF&64p!3~+h`tz%h6M6Bj;XsHixdM89@I4FPs!C>SSt2Lba;W z2!rFna>+V%S@vRXeDZ$^19G+f-QqJ+|v^ zw;y0VwznTTZ96XB#^qAD(vTyQW5czvc&78(MYt&lCJWxoh}RR$E@bSdMzIzg%s}JH zI*pai*bw!=Aea!02#5WU_tZ^`P{w5SZDz}FZT+>|@yLP(jybLnDelT%i?(Ij8+7gH7p2ui7-?fex98Ks5mL$!NeLe~sIJ zA*bTyZa#|n;506Y%|dmlXY$l0#edKoUCN&_D}~3kmd0-%13Dn%y0uV93@-`bg5*Py z78FX~gOt?%LbO4`h9%4j9k1 z&?~uJ%ucD9Wi#<$*PRmcug|uqk~7SRJ8h+(Ftsd52eS2U;+lS_ zcMz`0VKcEQ?tT(Eu@q78I{_QB@fMoGW0P95@8d9-x}c}Rs*;c4Dc(fO>>p8#U~U`* z)?ezwNt`^E{Tu&Dr6GU#$7*?e_ops8;@!vTV#~SWU`4XaqCla1W&J5$^*Q~t#iC7x zhwXMl$MWnoC-CQbNX%%Uj?Nyy&Ne1|`p|_HvW~aMf~^pT&lw*Pua_!z$8hn0);%!- zouNZKd%zogQ-;j!UxO|p15}*UeN?F7<@VzLT93};q-u_tO4(qxJI`C!=08Il6SEgw z*iGGyaOt+)ouysuWw0FIH90!4I`8DVI^WO9Y-M5}R^)&QJ?{T5GwVA#!UmwC?#V5Q zbU=tJ0U!-RnD=AC#cg0G**eNxfb9+VH-#T{_jdYEdl@GY-4+j%<9FIfOMoY`GP!OW z=j^w~`VRaCvjKRM8XLuUlRAPLFsDeia9hhaODe+Wy2svx^PyIO4ie6P`}O!wmS=Mg#{H> z&gK0x@nrc61FgV|WHDG&THX%XcgQ_EP~M8;QgDx>5#E~06SJj9|D;t+MP~_0b{3pW zAc8FwdF-SiLp0FF43w!6uu85yZH39(csrUy8US#_vdt!iE)p!G6P#>0GEJSGipROu z(!?m-RP0ApuIx+myV4(^UL60YKzH@cm9xuAM%v9WL3lS0VF!7&G;6fqs(w?2OU- zX7)u&lLfy*KQG3HY_&haYU?b?VRYF7bq#UpkcCoYk5s-cSn>%fwYB3!*Sj|qQPBXf zk?w%$LfR)~janeBeOJT}E;}W0Y4ZV%Wo(P9a5xEvc$uPos zvec*zV(1LHeMD8GOtbto!$kWAt=fwhQb2)ZJgFoYfz$vbyom~Kq8R3s68PENve)HJ zI3@8a^Z^wA2=+O4lTu~CPj6*aQ|0Vh2CwT;ah^i_n(lKOom0$2AL&~OgVoy9IMSu8 z@1BAv3vuW`%F-6Y6Iy?%up3;B0B3q~4mJ<@BKet@AAxV_UiD5-&}wQCK0g@H30JH# zwjnT*aSY@0-1Nr=>Kii0kR>wxUe?{M{qbjHL7y|=D3q~7>L{^aT<_o9N0%jQy7UNB zn7`Hhva<83h_w*t4?Fx%#4;fs8EW(X5kRlwZBLSWMftvurgZO5^-=!oPWlc^`j$i0 z;wfI^Z)>bd>)feBr)+_%bO~;KOQm5+3*ZM^uZ2DboEo98oskxu*lbJ3H=B7*zDsxC zZ18l9NN$S@@UX@dyvH*%Cpf1wgEb{?!+ZPfDCJxK?BV1Ay(R=aN%d-{>k_c^z46y9#ST)B^QNlu;yk>*X++bLD)sGnnqC?){=t z5M9})bO4|re+b-k06{_e=%4`s<)hq?atCEWT-*u$pcL_0^8_x@$gLRU-nx-zDf!{y6S+N3hF z^@%roJL&mq?q@4N`$DkL{D96Dd#M?<V*CL?=F3wc0P<-75vq{L6Z_*9$F{9N<~k9qARm2Y2LwaMOrlOrVSks|FY5BA;+oVO z>kcAx!q-Lo9-|i;yxC4={FUg89ibp>hVjk=olzF{U()=4>Xv*hAi=N{g?dNY+ne#| z$f~+L^9&=K$0w~I6TSW%rU{T|hay1Lg`(NHSp~n-cw!kSJOBS%u=MDU7Fkrx8+(W+~GHn_ZEf&5%8Y9AYxNAap>&k{)%9Ve>eP3QatEl!`JKwO)|Aa=IvqW8BDV_BS(o05^6f56Nq{Z8I* z-=v$I9qt-El-{}=Q9rU0Ot)-2p1+#_-iw zs}AfI{~;8(Ah{xKBgOm;C$g3jm5`AjIs>U(wy4z@Ipiby_qgZZML3vPTLQEBSBXKVNiiitdw*8`DT_Cng0{ z1Y2wU6f2%HhkiEGekLz!tj9|Yg);@?Y2=qBN{MmmF~`hG_lRT+{Ew$tr^I&OH&7Zn z2io?WmeU{5C(DwJmYC%n`}PibA77@%#|P&=Hu%de2!zvyjidck++wg9&*1d&#tJCu z!Sk@!KGYKVGHy7JsxL1x+Y?3;A0{?pU?C35mMF zjRoK+y9|8e$}0j5!d*sylsf`@%oqxdy>`EG$}H4@?K6TS_DlK@sVq){oy-%I&}dYp zps_^2CGsrra(E^DSyxT;d}j*+bA+F+Ub2~s8tT%?bUic^4CC96S&oTHT0jmOifn=@!iiZZIiDO}hx zo{Pjmuf;gwHxXNj?M7&hqtn)q{n)F+*~cTt$1(q%hBIg{+?yAL8^)cu%TVaW!PScC z#q|=c3=&P1OL2Q;r{O!qIQgK-7UboP7Ln3kk0PH;RWd;wbOXU8TgLNq+w(*Y2ao5< zlOi`O{LM%bkw0z(+qE$R@i;kda$nd_y$Z2c9b*UIOm@a=rLoi8ZGIWCF4?2!Q*!+i z&&1S2s;|Pf5cR>8%6~61E>NU#J}hwa9{h)o%o_ybf)b-*db2X~Ut4S_TJKl7AyM}) zjG{*M3!|v$RrP8_Lrf9CD*K&w1C=^pg~EW3+zEtHKV+U_U9^yJtCl$H zOgnOMF&8lIje}HIQG&LgO&OrsOhvf`Fy$4Ln^A1Sl{t3qpUiZ7g^uDR=qEzOQh5b9 z7SR##P*N=?8+&oyIi3x}3eyl5$QA<+hyOvr%?uKuu9vI#*6B(DQBCs|H3oOWr8qHkdOY1wp`csmsEW!ivflZ-`0&?hllE2yXln(uS z)>NF>a=T_HUnBD%X9J?yM!(KAGoXUPa$d;Mdqp$sjxHGUg^YSL`ueko# zTJ0V+#`ahQV|M_!INsmO{XQK&xHt2@;T~dX&>mep1EpI?h)L)wD5jp=SGGmlx4!xA z_2b`+_sC%3$;P>AmMGc)`PZphY4zd&ayC|0=+Y}~w>ONJy(hGzDO5_)7XL2z84OE3_A3$^@pAsWPF| zYt}ev4kJZgyuR^{8!h-^D%Edz*7Ge=P%)G57^rvDEv0)OW-Qpz|1K5BKerl7y_}`2 zlHmL;s^u@~Sqi`Y<|3V@wQ_fwnoX;JXrn)f1mU#$MHF&yx2tk~G9XRjj|!=-SaAGQ%10D8B};Pu8`C`HEsG^X z_%EP&%Bun}#sf3Pv$%VMslSmj2GgHFeqc(37ur+lJh4cC16GaD&eKJxJUz)tq<1kj z>#D7vf(VJPYdK>Mz%KRIn&jQxn=L@%c@r38zT9ETs#k83N8n{Pi3C*~4KyV_J~SpL zC25e_u0d8{XH*NxDHtmnY)_1pclEgMI6Ie2WQ`Ua!1+chj2H$^z3?GsjBtX!4)ZR# zj}%&~etGYzGDv(H-E+gr3OwdW*`3;SH^|86$l8fL?rYZ0QXLTVbI#4^6A>r*iD)+( zArndMh?1Dq)J!>kT%@abkXze>dzsiHh$!u>Vu5(Ks8`Z21&A&v;h5Mo2cFsPkcs=? zFFo5YBxl#V>)U6oy(=cAW6GEhOeDH*sR)+>EfhWJTV80Vgd9%J8C=+MK`9`QZf}lsb7(&)K1{T}RgleH(@~Wzz3XrU_Fj16st0I* zQ8I<+xQ5aMBTVZ=0*h7=0v=Jz~dyB*lBis<%QWdRR05{BvygG9JQ2;nA z>If!JH{~K^LeZs_^az`%Fu2SS^9 zW-U;PfB%FMl;&Q5pUEb7QLnOg!ESW3467;fCYFVW+Z5bi1nElU5Wb>lSFb}hihgs1 z3~rAfn21$LJij|->s*KOg1)m^{egaS8Hx@f*XN322>RRBCH0|yX@>8rjTcG@Wpx#= z;%DJ8yBxqMn5`;jQmL4x|BGTRn#k2+(`Kco>8c(kM` z1J$F)cEQ~${P#=A`Zy`_y+j>ngzpRu9FJQh(%P0WQMNUh&~i z?h9;u3r4ON#MfeMMZbBX&9H9veC8T=Ppj^>uk30cImeG28q{5GXH**ay^|{*ZJm^{ zk|YMo7So&DE9A{tsD4k&Wh)audx@%SG8aLAQd#-6F@<$_EAYyfFN_lx`q;*1XxdDe z3@j;DEOlDeDOk8>=$0m;Y|e7GtWzwiOfGVd@DnGRrs1{=T-)@v_Tkc_&mtFgZ0LvW z;^OeZj7sWYn*~{;V}ug8Mapi|iOh>$=zWcBv2u$-#Et0%JT^}1dCKV}VFgIZBj-9g z%yQaa)otkbZnE_lNmZzehFI)s=!gWFGdO(0roZG? z2;~+P5Ym@d+7C9sAvtgU#~SQh*>T}*N_+_c$tsk`5YZf&uDQHsJ6RNU5i>j_0N=2` zPn`Xy`_N`UYbDBxBcgkLQ-mv$+bcw|CD8fg5*xQSJN%V>oC4sPNJn8EbP|Pok;x<2 zqTJn>Ge3b9+D8_cOV%BnnHk%wd*ylO$6Jy#yg}^@V+X=Gu~vxJ!~duGK>YfDH6Kpy z^mr#%O$l(xc88uNBt1d0dEY`B{hB>wK4A~%W#F(*^PNz{lXBkwOA+PtkNN$=|G$lG zH9y2ZrN;>L)Q}ENd?z?pQhqYX!wGn%xB_kUrB%Ixz$UXeVcWAaQcX(zLh`z7z(XnF z%!QBI>N|(Uk2H~<R42pZ zjh_G{gLE6u)u88mHBhs1aTIv##o3i2QGuChU(*Zh_s0C=(JnI!5aiumASugPsP$fo z*g2hlD7Q`19G$7s@uEFIXCojzgSRXZ++HUj%;lRNf~U*chz=PCCAS(sz}kp#(}`}K z81@8z>3IDkc{YY9@RYfPw73Bhb*;;**<=h5DAM9;gjUXO)}flDZ)RD%H6B{nFq_BQ zxbuD7JgZsy;xbLCfx?PELgbg^E}vYO4N}G!Rr_1Pyqw(Kl!9#pk{IZ&4}P}H#;E_!}oh*L_Dc2r_P5J^`@jRij^BHfmF?N zYl66B0|#w1S_tbh)V}bLI?BGr$1jWZq0uBxi@h7I$C{HXcTKVK780ty1H3{}5akmDP3St?~;g~AZ1r!oFn zOY84iS}kbZhs&N&k|+Zb3hB-Wvq7%C04iI;0K4X|mf&wy<;xyeC+y3fi`LSwmVMAH zcjD5Nb#T}krJ5A%q5z#Xo$|y0nTGB1SN@_VgAckP&E2CTvI|S{9>P>XHD@JadX3D> z78Ghl0ikm;Ea8ETti;4A2KXKuAD(L1ap0Q`Ro3)wOP&~T(t&=nz;Gowgd_V;-gm$ z!`lQ-*Wv)-_-)V#5;c-OqPBl%;syG2te8`_UA-$nN-7?;a}MdoO3Z3$D#rARm)VQ- zhH`bJxN1>t1ZRB#V|96OgKjg?8gS_ud)1Eu8H6fo_GVss-Xm-L!Q!Aj^fAK1RVkLV z4KS3L@E{)H%ht6ep-X+)@o+B2&xx?h%_sD_q3H|}h=Qd8<3VcGjla!8+mV$7gr;Y( zX?r$3FD-ElIUP!>^6eL3@_k#9Qb4C18g-|?&Ro=EB#NL4QF(et3b3ZhnvGWpI~Q%B0gUx9-9j)AA$gA^4ost8aNN~j!>skmTG&qnmo z1QIF@Lj66(iMlif3uJ&bFMZV|pRYXBO02aV(@}DOxx|U;!Pj;Sri;c%oPigktfVDJ z9Y00wuH2qY#Z%%c5NYo>9w_;Z4|Y$_y6L+|TxqpJ7f@UQA;BU}x#+lb6O& z$qESlgjG2rMA1s9t?$-}i4|}o5c5ZFIx#40nmOh8@lmAs@j(KSLL|PB$!U-RB2eL1 zs)g~oo*G`F|3teLO4KFEmvWV$E*F?{8O4nzC;S;x6G5M5CPGFFpoXkJ9G-}`^6>-s zeG332&|xuTELhoDccE~`Zi(ZCHJZisMu0&_joUZxfz6F%XW*mjjDUR@6t%R?1RlMe>)8)wE#kGCAvg3DD@! z<{w%D{q(taf6$Y~X+h5=m!_vlxyrGIsF^fqK%V(;z7&h4S{@fG`eS9-y9ci<)pjiVELHR5<4g%IcTCm|B|G1Sbd zFPb`=={ITDKZi-4*F?sO$1+iII>h9Tpu;6-p4^)N&gC{ZFU;lU&cr`Hc`!z7a4n@~ z-zh)XQ!~||1YPDaLArM`wIw$ zxtyv#(ThUK&cdJU{LGtbUi)$EvWJcw8g}=$goO}xcV#S*tOa)@-OtE0-8C%m@lqW* zWUExO(&LI=CMDn{--h*5P>|z1L&AzODg5sbUHoI#Fq_KSd8HJMlq7A7=?_(z3==!$ z=(b#9Go@w3=Ats8Ba=tPm6a)E-sC)zRt{eL1#-uWjyB#dA}F4`<{9D;)qmkI9}5+l z1x9Gpe>DF{VCz*hHkT()%3A4_i(gQKbjiy8a)=ns*;bpdQUQg*Ek%u->Jn27|GIJZ zadO%5^rm-pUJzHvuc9K2bKz|b;Y3*%!P%;ahQyVnK|X-p})fXOJJVYPtj;)EUl|x=sPm2f6fH@!^D}6 zm(9B%qsU%mTU@gKh115UyFyww(~fkbh}=B5t3I|hd)%&Kf!e@pGvE8{)sO=*outvy z*QQYE4L_YU9$N27#cl=ZL+a*?=1`wV9*Th}E{h}v-=pwR#wC|P6Q?6(SRwt~{>hfo zP-GK?e`-`K`qXX~GqnE@N}7l;77-Rw#_%0IAK$6DHU?0v^-qn8 zA$s3s#RNg|2nW0;#c^?9X0)z*F6lGl~n zBD061JW3}hj*~kz2z+qbR&9o}jNg%7-&Tg^QGNMaPzT#lr;;*HJ44!^_U~&$zd3#w zBf1xm)iFPJLAE3I)a*uVq?HI@AroK8D9dDeC=9}dEwcz85};HF8Zn%n4HYp0Un&X{ z7C{Xx4^EV+VhXWF(}%lv~`+{(}7BXdhEYE7xWLjeq}LKdoJ<6fbB z1e&Z9W9h>1oM}#UCSriQLOoZE=2j5~*A`&Izz|}`x9fR( z8J|}+eLhxwHm7M`D*Gg5<&9M1n&06@R&kk@$K!^^QB7W+^oTs$@m{Ei8vnOSbYF5>qDUR%qd!G{?|EcIzZi+A zH0l@)jpyfiuB4gJ@iy`~YTeId3^)dHF9zT+1igr3Qk5SA*_;OorjEdx+5Q*E*gZ)# zgbJT*qJnmlQRQ%F@gvc<1%+AIGB|H~0B{lAjWqg=OEDRQU3%$i-O3i`NHaZe^So5hmn>u`* zM{o@PkmzKM2R9v~#?5O<6W+0mIuExv_X&a9QO$paEpV8dV7}zX)}$O&A2^f)$uNQh zPO&5o6rPJ$X^$Vl+SZdS@IT~X9TOlwZ!v9mJ{nUR!hJu=noCf-Kg;$EpSmL#P3=_y z3RU*AG)tCKDq|P#-en49uSYUfo284YSwGnLd(ZKX$&ug40$UZb&WzZ?iHzj%_7F0a=6+^OX6?p7LHDXkC&neI? zd_t{Vuk6xk=k_#O$;5rsHXbm${9870O~PAR^Ip(tnFw;%z9i~pMN=4%>x2+eB%`yD zkY>hkbMRCwMeC_asBYswgOP>?)eRbju>~&nXJio zMh7?sphwSvAQDmqMlNOBIEr*W*0g_=!oywov=l}o(J z{o_zvWc-jCmx5?nc#hMs3}Wg~NFc}?gkpk(Ck{&Jm`^D&PnpHMG*?y(n#)yUZ*OP( z6pQgck=NeiXdcSxE8AW5RwZ#EeFZmW=}sGNf$hcmJ_+PStPBKt<4y3tzpda#*rs7) zSvaBz6vQp9zn~VbM90>`}7=Qf2gK7qJZX0x1r76|RHx zQO7!`kzzalzo@SL2Ip^qa?5gq-3&*3+v%^KOY#7zVA4)MK)Asq0ZPV%_}zM=!ny?4 z=#IuHM|FxDqQ`{FGIs6J3@n9XH1&__+bpHUV8lLBb_Nm^7zr7>Fyl-Hat)fVG93e4xO(5X-s2?+x^r&F}pHdb|mR_I#c-5F(#Km*|gWSbyC4Ysk zdCTN=?*xO6*#&lwElY^9D`I8~_7tOZ?@SD%265X1)WnLa1?ypbM z?^SPJH+Qv<^{Yi5U0!ZFN_LwA*sq8ImeC4hZzr7S_UMrBnRtY`XA+HjkPI;q z^wV7CtCGF6`dcg#=qlJ+hK)-$DYrPd$u;)W*}3CE4n7?+*i3sV=6eNEVW(h+;x!w) z;e59=EGl5rN!XD`GAf*fG(sH3I>>Z@KlSw>(iij3g~4k7kmaevmwuOHg_gL-nFQH2 zPg~iIZPpz29Fa=qFYWsjZDcZ8Qra*Z?U&+?kIP2GGWc>RNK$@Lpn_@(ZHOr0?e<8Z zw*Oio!ZhDIT1!}zOBvnEU^-3%(c2jp@$@4e<6T*AGdbTWNdlcCm!({b4sb?cD(D0Y zXCd?n1Z6h+FPmRHFhXoN*V5zuqkI6_i};VSYaC4s zVM2gbbnU*9PEfUc2(ygm(uOOAWc@!T2Sc&SjieREM#(WV!fC4da0?Edhve7c8H(lS zCwxcer-9=@f5ZRHVnh|k@iJlM74sxiOUnGo3Ijs`$q!@q90bXi+imZZ1QU%9{{cC` z>N>pY$$sUp;Q}<@beK%F$90k^4cn@PqnMiG)oq ze!>k9HR~DF3Y3huK*_x|j)>Y8+Gy2I#de@de`h>3&{`-xR4P-{$o+7(2d%wHh^@r2 z<9wOV2Vti!b3co7#eHFaOf<7HzfYjIqzKmh1VR`F0&2gL?6Oc>#Q4 zBYb(Sczo>M`$pj%>>|h5SLJu-2j4*aIMGREet2ie@8Cm!|{a!ivJPF4}w@Pbx zA-*KTWl!`RkOUBUT1a`$>i?X!R0-*0U*+*Vkq8ExjDtf8CICY6|zX`JiMk zA{va4)d+s9L(%z1gYa`j-@z79Ze>cBD&XU?cIoAZmKE^2W0*p_(i(OWyuVVgI50@# zmVYv4VM>K7bj;P*(X@L(^ZbRUiMv*T&CI)|+w^w%kn6T)^$Fi&kJc=HJJh&{f(aBA z`UQf$TQo}01o8`@ZHqi4MHfYL1uohWsk<#3<*@wl5Ky^6pS~43KZtuCiA7q zZp?CJBIL-A9%cw-u!LB7{TMyVugDd~(JMQ3GgBfsz4M=U`{GSP9lB8$H6m~u zAP0a8GpKu%%jjyRGQG#(YL6R<|)nMlm^fr8+H>wre*g zVllhS>lTlAVGo7BJM+4OgR^b+Sh9OnCDX?F(r-Zv&j@45{K_Lu2K11^+Oj6KzE zv_`#vHZb3bqrfs|xqIiB=pNNM{!h1Na<{|qS1ed>w(a_lG@5uufC*zXizf^yjodjg zLO`M$?*GZs)rRYq%4v~7lahKNLed=hpC`p0ELN;n)7=7loUOFf_2td3|c zw+9a%`7duvO>7QfAQU5>HP%gM70Rl#T`^LCzb4r6YFSi~c}YW62i)fK4z&p?;2>%e z_ooYUFDV}0b8#f(vC4%I$}9gPC~5q8{VWVe*Nvy*{E_UMD^(5rq+|KjCooe2$XJ z6&Ke_M|)Q#7FTwgZmLi|9=z{Q=Z)VJ6W4T8J0T$?=rFo@T%eP-pSIZ`!Ql>!q#o$6 zW*(fShM^oo4BJc8Q_w_N2Ei$ZX9gxI7a?(LLCj<@;KVz3h#?6Kh7RcBg)Bwa{+V>x z*$={5nW*GmeaZQiZR*zZDRG%y$PQK1IhLeUrqlm7;GRzP>j3|q2(;2`YuP08eiOtv zet)}we_Nm3rlH z!do=VtCLh%cJL;1P}fsc3a$qivIZZeT%UDBTQw{bkPBTTfvpHQhpN?Ib?M zABMzX7A0bfcs}Nj>BDSBs6{e+XIazEUepXr+>WOJn56NnL5mE|s*t}LEkJwLKkZlD zjlmtkKFD>}N7*a~wHcf8fuLHRHeKh~Ti=g~HCmd7t%Z)5+rE}6=z1R#~P;i|0{;TEJk%D8H6qU(5`5&Yvg3E)+Y#W%AwNUf3z+=3s0Ka3~td#AJFy z&dXGDt4O&(T{L_u6qiLLQYD7+YD$NsvnT^S(kb?fYrsloMi4@N+|Y=BFRf~cV0)k$ z<9r(-vK_4J10kk)ykNbXwkmMf(neT8lWVoyJ_rL{`|Sl z#;xhw4@)h`oTdEgCC)-J=|&e;JW3|4+(rwU=Zq-K(-hd-q8QbQJ~0hmM;4`|K2Pf| zHI1M-R|kyHU_e|=3-&V3A9#n!={e-iK^?JdvVRF|^)@mqVrGkD*Hc4^u1E|7+k8bZ z(AezClz%DIkR0Be_)ats;u%~1w(IHj0)I_+oQnEcj@ewr&D%+a2S~yFsZuasypZ^8 zp)3cu+gY}sd$rxNSg3hSzfvf&pQT;$xN8BR^+9UG>`lIi;g0FG7wI!;BafCmOgLwKi~NoARa$ z4$I&!q_>A+U=axwTqxv502(v~3+2R#0k0<#ii9tT$zKJqw=k<*8V-pd?$b#n=gD*p zR)8rR_BC9s-h(3aMvs1%$FVV;29%j9w`TRGv$CILrZrhZv~DIHk};v@BMWBiqTa|f z{wqae=|Vw7)G>tsGtBfQR)sxNAE_s~2glrznjN>6+HU?9IXMh6qt2nPP1VM+Rlskp zEp@M~?6233@8FVV{u?aJus-o|X#yo&7zItZQd+NaDAVHqq0j@F7bcwnB{}dj;>tbE zv2@b<%N{8tK_Cy`@%N8>jMLu|(g-#DLC*SiEoybGI?otLqGKa(F_f(Nv4#NT#tp{O z*x&IP2Ji3qtkEkH^r@Zkj6({A;?&LQ<<@VWXLRW)h|f~L+zWAUAW-l3OWV2b{^zN# z%JXH*EC4>Lr;d7y^JQB#XIA#UBhm);zVlCu`bX4mmdA{>oV4cZcVIvkZv#(xB~!Lg z+45<#Vf!%^K=jnJ@Vuh_p#3mqw)p1x+|Ejq6Qi58t;O46=Iy!s;9cX^qwGVKh$ZP1coAy_!WKq= zlxxRXhjtoSH=R0p;fj=}=pLJ;3Bo**%U*0uHHV`rWb~s@esJ`l9E!}sO9P5rKGcwu z?>AsbCC)u?t$5$TH+SWmkBzdGdkQA}lJEH0i?UTAL#12JY=~a?ynvLbdFlb~a=TE^$ip2>Qa#IR48w)yknnfGP|EgMei6x22 zvJHz3!*Ug(UrW$U)J?L%3oE@?LK_#cT)F|90MGg)ULHf2(Vtp>5HKo?QXQ2{C@A%% zfK^huA4{0^W02=6%n93G;!OZOs8Ocdr%F9KLMi^~juGQ=X0`iVGZJ9o@F^I_u5Vuz zTIKtg$GCqJ3$=mOJfy3`s)1N=N^zWe Date: Fri, 20 Mar 2020 13:52:24 -0400 Subject: [PATCH 086/107] Removed some debugging RUN command --- drupal.Dockerfile | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drupal.Dockerfile b/drupal.Dockerfile index 5b572df1a..700a8b4cd 100644 --- a/drupal.Dockerfile +++ b/drupal.Dockerfile @@ -81,10 +81,6 @@ ENV PATH=${PATH}:${APP_ROOT}/vendor/bin \ APP_RUNNER_GROUP=${app_runner_group} \ APP_RUNNER_GROUP_ID=${app_runner_group_id:-1000} -RUN ls -all /confd_templates/; \ - ls -all /drupal/; \ - ls -all /drupal/confd/ - # Copy custom configuration template files for PHP and NGINX RUN mkdir -p /etc/confd && cp -R /confd_templates/* /etc/confd/; \ # Copy custom excutable scripts for drupal including the default entrypoint. From 8724307465bd015d25343a91429db819a0ca40cf Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Fri, 20 Mar 2020 13:57:28 -0400 Subject: [PATCH 087/107] Removed commented code. --- scripts/drupal/init.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 95339e78a..3796b4f7e 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -2,9 +2,6 @@ set -e -# Print the number of arguments. -# echo "$#" - codebase="drupal" config_dir="$PWD/config/drupal" scripts_dir="$PWD/scripts/drupal" From b9657a93f0dd9efb3d292f1f1c67d00b8425d012 Mon Sep 17 00:00:00 2001 From: "Noah W. Smith" Date: Fri, 20 Mar 2020 13:59:03 -0400 Subject: [PATCH 088/107] adding README stub --- docs/MVP2b_README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 docs/MVP2b_README.md diff --git a/docs/MVP2b_README.md b/docs/MVP2b_README.md new file mode 100644 index 000000000..bdc8c7037 --- /dev/null +++ b/docs/MVP2b_README.md @@ -0,0 +1,25 @@ +Running the containers +With this repo you can run drupal using drupal/recommended-project or islandora/drupal-project. I have added aMakefile to facilitate with the setup and running the needed commands. The steps below will download the codebase, setup data/drupal/ and run docker-compose. + +Run make isle_codebase=drupal to create codebase with drupal/recommended-project +Or run make isle_codebase=islandora to create codebase with islandora/drupal-project +In case you have composer installed locally and run into the composer memory limit problem you might need to run COMPOSER_MEMORY_LIMIT=-1 make isle_codebase=islandora +You might need to run docker stats to check CPU usage for each container. Since AUTO_INSTALL is on it might take some time for the drupal container CPU usage to come down once it's done. So give the drupal container up to 5-10min to complete the site installation. Then visit islandora.localhost:8000 in chrome or if using any non chromium web browser add islandora.localhost in the /etc/hosts. + +Bringing down the containers +Run make down to just bring down the container without cleaning up all the various docker assets related to the docker compose. +Or run make clean to delete everything from the codebase, data/drupal to all containers, images and volumes associated with docker-compose.yml +For a "light clean" you can also run make clean_local +For more make commands please check the Makefile. + +Interacting with the container +docker-compose -p islandora exec drupal drush uli: to get a one time login url for admin. +docker-compose -p islandora exec drupal drush cr: to clear cache. +Notes +The drupal.Dockerfile still using registry.gitlab.com/nikathone/drupal-docker-good-defaults/php-nginx:latest for php and nginx base image but this will change once we have a CI to build images/nginx-php/Dockerfile. + +I did disable the SOLR service. + +Also it might be a good idea to move the traefik service setup in it's own docker-compose file cause ideally they should only be one traefik service per local host. + +I might have missed something since I was trying to avoid to add to many changes at once. From 5dffa060bdf02768242f48c8b8dc3fc9015eb33f Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Fri, 20 Mar 2020 13:59:32 -0400 Subject: [PATCH 089/107] Removed unused variable. --- scripts/drupal/init.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 3796b4f7e..cca8c5b2d 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -4,7 +4,6 @@ set -e codebase="drupal" config_dir="$PWD/config/drupal" -scripts_dir="$PWD/scripts/drupal" composer_install_run="true" current_folder="$PWD" composer_general_flags="--ignore-platform-reqs --no-interaction" From 46453b809159b5a5d3d8c4796a2769ae7e4f6549 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Fri, 20 Mar 2020 14:18:11 -0400 Subject: [PATCH 090/107] Removed more unused vars in drupal init. --- scripts/drupal/init.sh | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index cca8c5b2d..f3719fd52 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -27,9 +27,7 @@ function help() { function download_drupal() { local args="create-project drupal-composer/drupal-project:8.x-dev" # local args="create-project drupal/recommended-project:^8.8" - local flags="--ignore-platform-reqs --no-interaction" local codebase="$1" - local drush_require="require drush/drush $flags" if [[ ! $composer ]]; then fail "We could not download drupal. Ensure composer or docker is setup and installed properly on your local host." From 3e01df5f0a8c34cf2e87fec0de30b55413c5be1e Mon Sep 17 00:00:00 2001 From: "Noah W. Smith" Date: Fri, 20 Mar 2020 16:20:16 -0400 Subject: [PATCH 091/107] md syntax on new doc --- Makefile | 2 +- docs/MVP2b_README.md | 45 +++++++++++++++++++++++++------------------- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/Makefile b/Makefile index 06369ab03..63511353d 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ docker_compose_project ?= islandora .PHONY: help drupal_init up build down down_rmi_all down_rmi_local drupal_clean clean_local clean -default: drupal_init solr_init up +default: drupal_init up solr_init help: ./scripts/drupal/init.sh --help diff --git a/docs/MVP2b_README.md b/docs/MVP2b_README.md index bdc8c7037..2deca034c 100644 --- a/docs/MVP2b_README.md +++ b/docs/MVP2b_README.md @@ -1,22 +1,29 @@ -Running the containers -With this repo you can run drupal using drupal/recommended-project or islandora/drupal-project. I have added aMakefile to facilitate with the setup and running the needed commands. The steps below will download the codebase, setup data/drupal/ and run docker-compose. - -Run make isle_codebase=drupal to create codebase with drupal/recommended-project -Or run make isle_codebase=islandora to create codebase with islandora/drupal-project -In case you have composer installed locally and run into the composer memory limit problem you might need to run COMPOSER_MEMORY_LIMIT=-1 make isle_codebase=islandora -You might need to run docker stats to check CPU usage for each container. Since AUTO_INSTALL is on it might take some time for the drupal container CPU usage to come down once it's done. So give the drupal container up to 5-10min to complete the site installation. Then visit islandora.localhost:8000 in chrome or if using any non chromium web browser add islandora.localhost in the /etc/hosts. - -Bringing down the containers -Run make down to just bring down the container without cleaning up all the various docker assets related to the docker compose. -Or run make clean to delete everything from the codebase, data/drupal to all containers, images and volumes associated with docker-compose.yml -For a "light clean" you can also run make clean_local -For more make commands please check the Makefile. - -Interacting with the container -docker-compose -p islandora exec drupal drush uli: to get a one time login url for admin. -docker-compose -p islandora exec drupal drush cr: to clear cache. -Notes -The drupal.Dockerfile still using registry.gitlab.com/nikathone/drupal-docker-good-defaults/php-nginx:latest for php and nginx base image but this will change once we have a CI to build images/nginx-php/Dockerfile. +### Running the containers + +With this repo you can run drupal using `drupal/recommended-project` or `islandora/drupal-project`. I have added a`Makefile` to facilitate with the setup and running the needed commands. The steps below will download the codebase, setup `data/drupal/` and run `docker-compose`. + +- Run `make isle_codebase=drupal` to create codebase with `drupal/recommended-project` +- Or run `make isle_codebase=islandora` to create codebase with `islandora/drupal-project` +- In case you have composer installed locally and run into the composer memory limit problem you might need to run `COMPOSER_MEMORY_LIMIT=-1 make isle_codebase=islandora` + +You might need to run `docker stats` to check CPU usage for each container. Since `AUTO_INSTALL` is on it might take some time for the `drupal` container CPU usage to come down once it's done. So give the `drupal` container up to 5-10min to complete the site installation. Then visit `islandora.localhost:8000` in chrome or if using any non chromium web browser add `islandora.localhost` in the `/etc/hosts`. + +### Bringing down the containers + +- Run `make down` to just bring down the container without cleaning up all the various docker assets related to the docker compose. +- Or run `make clean` to delete everything from the `codebase`, `data/drupal` to all containers, images and volumes associated with `docker-compose.yml` +- For a "light clean" you can also run `make clean_local` + +For more make commands please check the `Makefile`. + +## Interacting with the container + +- `docker-compose -p islandora exec drupal drush uli`: to get a one time login url for admin. +- `docker-compose -p islandora exec drupal drush cr`: to clear cache. + +## Notes + +The `drupal.Dockerfile` still using `registry.gitlab.com/nikathone/drupal-docker-good-defaults/php-nginx:latest` for php and nginx base image but this will change once we have a CI to build `images/nginx-php/Dockerfile`. I did disable the SOLR service. From deb5773c44ebaa857bd86bf0997e4793761b1383 Mon Sep 17 00:00:00 2001 From: Aaron Birkland Date: Mon, 23 Mar 2020 11:36:00 -0400 Subject: [PATCH 092/107] Fix drupal config logic --- scripts/drupal/init.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index f3719fd52..43cf986fb 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -185,7 +185,7 @@ fi # load the config here if [[ "$codebase" == "islandora" ]]; then - if [[ ! "$(ls -A codebase/config/sync)" && ! -f codebase/config/sync/core.extension.yml ]]; then + if [[ ! -d codebase/config/sync || ! -f codebase/config/sync/core.extension.yml ]]; then tar -xzf config/drupal/islandora-starter-config.tar.gz codebase/config/sync fi fi From f38ea6ad0af93904827e37110bd50f1f295f6fd7 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Mon, 23 Mar 2020 12:54:19 -0400 Subject: [PATCH 093/107] Fix wrong messaging on package install. --- scripts/drupal/init.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 43cf986fb..cbd23d46a 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -78,7 +78,8 @@ function download_required_packages() { local version=${versions[$i]} # Only installing a package when it is not available in composer.json if [[ ! $(grep "${package}" composer.json) ]]; then - echo " Requiring ${package}. Skipping." + echo " Requiring ${package}. Downloading." + echo " " $composer require ${package}:${version} $composer_general_flags else echo " ${package} was found in the composer.json. Skipping." From 24102ee0f8bcc13efcfbc885486e89f744903cab Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Mon, 23 Mar 2020 12:56:12 -0400 Subject: [PATCH 094/107] removed the tests scripts for now. --- .github/.dockerignore | 14 -------- .github/docker-compose.override.yml | 13 -------- .github/workflows/ci.yml | 52 ----------------------------- .gitlab-ci.yml | 41 ----------------------- 4 files changed, 120 deletions(-) delete mode 100644 .github/.dockerignore delete mode 100644 .github/docker-compose.override.yml delete mode 100644 .github/workflows/ci.yml delete mode 100644 .gitlab-ci.yml diff --git a/.github/.dockerignore b/.github/.dockerignore deleted file mode 100644 index 8b97d98d9..000000000 --- a/.github/.dockerignore +++ /dev/null @@ -1,14 +0,0 @@ -.git -.idea -.gitlab -.vscode -.gitlab-ci.yml -*/mysql -/mysql -*/files -/files -vendor/ -node_modules/ -*.sql -*.tmp -/data diff --git a/.github/docker-compose.override.yml b/.github/docker-compose.override.yml deleted file mode 100644 index 3949ae8d8..000000000 --- a/.github/docker-compose.override.yml +++ /dev/null @@ -1,13 +0,0 @@ -version: "2.4" - -services: - drupal: - ports: - - "80:8080" - # Removing traefik service functionality cause we don't need it during testing. - # https://github.com/docker/compose/issues/3729 - traefik: - image: hello-world - -volumes: - mariadb-data: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 7c3107ee9..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,52 +0,0 @@ ---- -name: CI -"on": - push: - branches: - - master - - development - pull_request: - -jobs: - test: - name: Test the main docker-compose.yml default usage. - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest] - - steps: - - name: Check out code. - uses: actions/checkout@v2 - - - name: Checking docker version. - run: docker --version && docker-compose --version - - - name: Initialize the codebase. - run: | - mv .github/docker-compose.override.yml docker-compose.override.yml - mv .github/.dockerignore .dockerignore - ./scripts/drupal/init.sh --codebase islandora - - - name: Build app image - run: > - docker image build - --tag app-image-test:latest - --target dev - --build-arg code_dir=./codebase - --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b - --build-arg build_environment=dev - --build-arg NGINX_LISTEN_PORT=8080 - --build-arg NGINX_SERVER_ROOT=/var/www/app/web - --file drupal.Dockerfile - . - # - name: Run docker-compose up. - # run: docker-compose -p islandora up --remove-orphans --detach - # - name: Wait for Drupal to install. - # run: | - # while ! docker container logs islandora_drupal_1 | grep "nginx entered RUNNING state"; do - # sleep 1 - # done - # - name: Test loading the home page. - # run: > - # curl -s http://localhost | grep "

    Welcome to islandora

    " diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index b1d677521..000000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,41 +0,0 @@ -variables: - DOCKER_VERSION: "19.03.8" - -test: - image: registry.gitlab.com/nikathone/image-utils/docker:$DOCKER_VERSION-compose - stage: test - services: - - docker:$DOCKER_VERSION-dind - before_script: - - docker --version && docker-compose --version - script: - - mv .github/docker-compose.override.yml docker-compose.override.yml - - mv .github/.dockerignore .dockerignore - - ls -all scripts/drupal/ - - echo "$HOME" - - chmod +x scripts/drupal/init.sh - - > - docker image build - --tag app-image-test:latest - --target dev - --build-arg code_dir=./codebase - --build-arg base_image_tag=7.2.28-1.17.8-0ceedc1b - --build-arg build_environment=dev - --build-arg NGINX_LISTEN_PORT=8080 - --build-arg NGINX_SERVER_ROOT=/var/www/app/web - --file drupal.Dockerfile - . - # - make - # - > - # while ! docker container logs islandora_drupal_1 | grep "nginx entered RUNNING state"; do - # echo "Waiting for the site to be installed!" - # sleep 2m - # done - # - curl -s http://localhost | grep '

    Welcome to islandora

    ' - after_script: - - echo "$(uname -s)" - - if [[ -f /etc/os-release ]]; then cat /etc/os-release; fi - only: - refs: - - master - - development From 13cbba1baee35d8f99cdd6170da0b7ab1967e2bf Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 24 Mar 2020 10:04:36 -0400 Subject: [PATCH 095/107] Making sure that the docker composer container is running as the current user to avoid any permission issue. --- scripts/drupal/init.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index cbd23d46a..2526c59c9 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -167,9 +167,9 @@ if [[ ! $composer ]]; then echo >&2 mkdir -p "$HOME/.composer" if $is_darwin; then - composer="docker container run -it --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" + composer="docker container run -it --rm --user $UID:$GUID -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" else - composer="docker container run -t --rm -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" + composer="docker container run -t --rm --user $UID:$GUID -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" fi fi @@ -182,8 +182,6 @@ else download_required_packages fi - - # load the config here if [[ "$codebase" == "islandora" ]]; then if [[ ! -d codebase/config/sync || ! -f codebase/config/sync/core.extension.yml ]]; then From 4d4d6c4edd624d4cb5a055d0913ad4d5056f8233 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Sat, 28 Mar 2020 07:03:14 -0400 Subject: [PATCH 096/107] Setting islandora codebase as default and cleaning up codebase in case it exist prior to composer create project. --- scripts/drupal/init.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 2526c59c9..d34e5ede8 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -25,16 +25,15 @@ function help() { } function download_drupal() { - local args="create-project drupal-composer/drupal-project:8.x-dev" - # local args="create-project drupal/recommended-project:^8.8" + local args="create-project born-digital/drupal-project:dev-isle8-dev" local codebase="$1" if [[ ! $composer ]]; then fail "We could not download drupal. Ensure composer or docker is setup and installed properly on your local host." fi - if [[ "$codebase" == "islandora" ]]; then - local args="create-project born-digital/drupal-project:dev-isle8-dev" + if [[ "$codebase" == "drupal" ]]; then + local args="create-project drupal-composer/drupal-project:8.x-dev" fi echo -e "\033[1m[INFO]\033[0m Installing drupal using composer" @@ -43,6 +42,9 @@ function download_drupal() { echo " Downloading the drupal codebase." echo >&2 cd "$current_folder/" + + # Delete codebase just in case a previous command failed to finish the installation. + [[ -d ./codebase ]] && rm -rf ./codebase $composer $args $composer_general_flags codebase download_required_packages From a0a0b95ff395ca8b4fa882977a88088daba6f72f Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Sat, 28 Mar 2020 07:11:36 -0400 Subject: [PATCH 097/107] Checking for vendor autoload to decide if we should run composer install. --- scripts/drupal/init.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index d34e5ede8..c3f534a28 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -178,7 +178,7 @@ fi ### # Checking if the project code exists. ### -if [[ ! -f "$PWD/codebase/composer.json" ]]; then +if [[ ! -f "$current_folder/codebase/composer.json" ]]; then download_drupal $codebase else download_required_packages @@ -186,7 +186,7 @@ fi # load the config here if [[ "$codebase" == "islandora" ]]; then - if [[ ! -d codebase/config/sync || ! -f codebase/config/sync/core.extension.yml ]]; then + if [[ ! -f codebase/config/sync/core.extension.yml ]]; then tar -xzf config/drupal/islandora-starter-config.tar.gz codebase/config/sync fi fi @@ -199,7 +199,7 @@ mkdir -p $PWD/data/drupal/files/public $PWD/data/drupal/files/private $PWD/data/ ### # Running composer install just in case the user has an existing project. ### -if [[ "$composer_install_run" == "true" ]]; then +if [[ ! -f "$current_folder/codebase/vendor/autoload.php" ]]; then cd "$current_folder/codebase" $composer install $composer_flags cd .. From 2b40dd3b12ef20af421d2a35f9b28e18b6a5e0aa Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Sat, 28 Mar 2020 07:40:46 -0400 Subject: [PATCH 098/107] Remove composer install flag and simplify if statements for config install. --- scripts/drupal/init.sh | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index c3f534a28..97c0cd075 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -4,7 +4,6 @@ set -e codebase="drupal" config_dir="$PWD/config/drupal" -composer_install_run="true" current_folder="$PWD" composer_general_flags="--ignore-platform-reqs --no-interaction" OS=`uname -s` @@ -90,8 +89,6 @@ function download_required_packages() { echo " " echo >&2 - # Flag that we shouldn't run composer install to initialize everything. - composer_install_run="false" cd "$current_folder" } @@ -185,10 +182,8 @@ else fi # load the config here -if [[ "$codebase" == "islandora" ]]; then - if [[ ! -f codebase/config/sync/core.extension.yml ]]; then - tar -xzf config/drupal/islandora-starter-config.tar.gz codebase/config/sync - fi +if [[ "$codebase" == "islandora" && ! -f codebase/config/sync/core.extension.yml ]]; then + tar -xzf config/drupal/islandora-starter-config.tar.gz codebase/config/sync fi ### From 3b3f96e08e27e8e8af6ae12f26375dfa2d115359 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Sat, 28 Mar 2020 15:13:06 -0400 Subject: [PATCH 099/107] Removing the snippet and using a pre-configured settings.php --- config/drupal/settings.php | 764 ++++++++++++++++++ ...settings.isle.php => settings.project.php} | 0 config/drupal/snippet.txt | 7 - 3 files changed, 764 insertions(+), 7 deletions(-) create mode 100644 config/drupal/settings.php rename config/drupal/{settings.isle.php => settings.project.php} (100%) delete mode 100644 config/drupal/snippet.txt diff --git a/config/drupal/settings.php b/config/drupal/settings.php new file mode 100644 index 000000000..fb1e9cf43 --- /dev/null +++ b/config/drupal/settings.php @@ -0,0 +1,764 @@ + 'databasename', + * 'username' => 'sqlusername', + * 'password' => 'sqlpassword', + * 'host' => 'localhost', + * 'port' => '3306', + * 'driver' => 'mysql', + * 'prefix' => '', + * 'collation' => 'utf8mb4_general_ci', + * ]; + * @endcode + */ +$databases = []; + +/** + * Customizing database settings. + * + * Many of the values of the $databases array can be customized for your + * particular database system. Refer to the sample in the section above as a + * starting point. + * + * The "driver" property indicates what Drupal database driver the + * connection should use. This is usually the same as the name of the + * database type, such as mysql or sqlite, but not always. The other + * properties will vary depending on the driver. For SQLite, you must + * specify a database file name in a directory that is writable by the + * webserver. For most other drivers, you must specify a + * username, password, host, and database name. + * + * Transaction support is enabled by default for all drivers that support it, + * including MySQL. To explicitly disable it, set the 'transactions' key to + * FALSE. + * Note that some configurations of MySQL, such as the MyISAM engine, don't + * support it and will proceed silently even if enabled. If you experience + * transaction related crashes with such configuration, set the 'transactions' + * key to FALSE. + * + * For each database, you may optionally specify multiple "target" databases. + * A target database allows Drupal to try to send certain queries to a + * different database if it can but fall back to the default connection if not. + * That is useful for primary/replica replication, as Drupal may try to connect + * to a replica server when appropriate and if one is not available will simply + * fall back to the single primary server (The terms primary/replica are + * traditionally referred to as master/slave in database server documentation). + * + * The general format for the $databases array is as follows: + * @code + * $databases['default']['default'] = $info_array; + * $databases['default']['replica'][] = $info_array; + * $databases['default']['replica'][] = $info_array; + * $databases['extra']['default'] = $info_array; + * @endcode + * + * In the above example, $info_array is an array of settings described above. + * The first line sets a "default" database that has one primary database + * (the second level default). The second and third lines create an array + * of potential replica databases. Drupal will select one at random for a given + * request as needed. The fourth line creates a new database with a name of + * "extra". + * + * You can optionally set prefixes for some or all database table names + * by using the 'prefix' setting. If a prefix is specified, the table + * name will be prepended with its value. Be sure to use valid database + * characters only, usually alphanumeric and underscore. If no prefixes + * are desired, leave it as an empty string ''. + * + * To have all database names prefixed, set 'prefix' as a string: + * @code + * 'prefix' => 'main_', + * @endcode + * + * Per-table prefixes are deprecated as of Drupal 8.2, and will be removed in + * Drupal 9.0. After that, only a single prefix for all tables will be + * supported. + * + * To provide prefixes for specific tables, set 'prefix' as an array. + * The array's keys are the table names and the values are the prefixes. + * The 'default' element is mandatory and holds the prefix for any tables + * not specified elsewhere in the array. Example: + * @code + * 'prefix' => [ + * 'default' => 'main_', + * 'users' => 'shared_', + * 'sessions' => 'shared_', + * 'role' => 'shared_', + * 'authmap' => 'shared_', + * ], + * @endcode + * You can also use a reference to a schema/database as a prefix. This may be + * useful if your Drupal installation exists in a schema that is not the default + * or you want to access several databases from the same code base at the same + * time. + * Example: + * @code + * 'prefix' => [ + * 'default' => 'main.', + * 'users' => 'shared.', + * 'sessions' => 'shared.', + * 'role' => 'shared.', + * 'authmap' => 'shared.', + * ]; + * @endcode + * NOTE: MySQL and SQLite's definition of a schema is a database. + * + * Advanced users can add or override initial commands to execute when + * connecting to the database server, as well as PDO connection settings. For + * example, to enable MySQL SELECT queries to exceed the max_join_size system + * variable, and to reduce the database connection timeout to 5 seconds: + * @code + * $databases['default']['default'] = [ + * 'init_commands' => [ + * 'big_selects' => 'SET SQL_BIG_SELECTS=1', + * ], + * 'pdo' => [ + * PDO::ATTR_TIMEOUT => 5, + * ], + * ]; + * @endcode + * + * WARNING: The above defaults are designed for database portability. Changing + * them may cause unexpected behavior, including potential data loss. See + * https://www.drupal.org/developing/api/database/configuration for more + * information on these defaults and the potential issues. + * + * More details can be found in the constructor methods for each driver: + * - \Drupal\Core\Database\Driver\mysql\Connection::__construct() + * - \Drupal\Core\Database\Driver\pgsql\Connection::__construct() + * - \Drupal\Core\Database\Driver\sqlite\Connection::__construct() + * + * Sample Database configuration format for PostgreSQL (pgsql): + * @code + * $databases['default']['default'] = [ + * 'driver' => 'pgsql', + * 'database' => 'databasename', + * 'username' => 'sqlusername', + * 'password' => 'sqlpassword', + * 'host' => 'localhost', + * 'prefix' => '', + * ]; + * @endcode + * + * Sample Database configuration format for SQLite (sqlite): + * @code + * $databases['default']['default'] = [ + * 'driver' => 'sqlite', + * 'database' => '/path/to/databasefilename', + * ]; + * @endcode + */ + +/** + * Location of the site configuration files. + * + * The $settings['config_sync_directory'] specifies the location of file system + * directory used for syncing configuration data. On install, the directory is + * created. This is used for configuration imports. + * + * The default location for this directory is inside a randomly-named + * directory in the public files path. The setting below allows you to set + * its location. + */ +$settings['config_sync_directory'] = '../config/sync'; + +/** + * Settings: + * + * $settings contains environment-specific configuration, such as the files + * directory and reverse proxy address, and temporary configuration, such as + * security overrides. + * + * @see \Drupal\Core\Site\Settings::get() + */ + +/** + * Salt for one-time login links, cancel links, form tokens, etc. + * + * This variable will be set to a random value by the installer. All one-time + * login links will be invalidated if the value is changed. Note that if your + * site is deployed on a cluster of web servers, you must ensure that this + * variable has the same value on each server. + * + * For enhanced security, you may set this variable to the contents of a file + * outside your document root; you should also ensure that this file is not + * stored with backups of your database. + * + * Example: + * @code + * $settings['hash_salt'] = file_get_contents('/home/example/salt.txt'); + * @endcode + */ +$settings['hash_salt'] = ''; + +/** + * Deployment identifier. + * + * Drupal's dependency injection container will be automatically invalidated and + * rebuilt when the Drupal core version changes. When updating contributed or + * custom code that changes the container, changing this identifier will also + * allow the container to be invalidated as soon as code is deployed. + */ +# $settings['deployment_identifier'] = \Drupal::VERSION; + +/** + * Access control for update.php script. + * + * If you are updating your Drupal installation using the update.php script but + * are not logged in using either an account with the "Administer software + * updates" permission or the site maintenance account (the account that was + * created during installation), you will need to modify the access check + * statement below. Change the FALSE to a TRUE to disable the access check. + * After finishing the upgrade, be sure to open this file again and change the + * TRUE back to a FALSE! + */ +$settings['update_free_access'] = FALSE; + +/** + * External access proxy settings: + * + * If your site must access the Internet via a web proxy then you can enter the + * proxy settings here. Set the full URL of the proxy, including the port, in + * variables: + * - $settings['http_client_config']['proxy']['http']: The proxy URL for HTTP + * requests. + * - $settings['http_client_config']['proxy']['https']: The proxy URL for HTTPS + * requests. + * You can pass in the user name and password for basic authentication in the + * URLs in these settings. + * + * You can also define an array of host names that can be accessed directly, + * bypassing the proxy, in $settings['http_client_config']['proxy']['no']. + */ +# $settings['http_client_config']['proxy']['http'] = 'http://proxy_user:proxy_pass@example.com:8080'; +# $settings['http_client_config']['proxy']['https'] = 'http://proxy_user:proxy_pass@example.com:8080'; +# $settings['http_client_config']['proxy']['no'] = ['127.0.0.1', 'localhost']; + +/** + * Reverse Proxy Configuration: + * + * Reverse proxy servers are often used to enhance the performance + * of heavily visited sites and may also provide other site caching, + * security, or encryption benefits. In an environment where Drupal + * is behind a reverse proxy, the real IP address of the client should + * be determined such that the correct client IP address is available + * to Drupal's logging, statistics, and access management systems. In + * the most simple scenario, the proxy server will add an + * X-Forwarded-For header to the request that contains the client IP + * address. However, HTTP headers are vulnerable to spoofing, where a + * malicious client could bypass restrictions by setting the + * X-Forwarded-For header directly. Therefore, Drupal's proxy + * configuration requires the IP addresses of all remote proxies to be + * specified in $settings['reverse_proxy_addresses'] to work correctly. + * + * Enable this setting to get Drupal to determine the client IP from the + * X-Forwarded-For header. If you are unsure about this setting, do not have a + * reverse proxy, or Drupal operates in a shared hosting environment, this + * setting should remain commented out. + * + * In order for this setting to be used you must specify every possible + * reverse proxy IP address in $settings['reverse_proxy_addresses']. + * If a complete list of reverse proxies is not available in your + * environment (for example, if you use a CDN) you may set the + * $_SERVER['REMOTE_ADDR'] variable directly in settings.php. + * Be aware, however, that it is likely that this would allow IP + * address spoofing unless more advanced precautions are taken. + */ +# $settings['reverse_proxy'] = TRUE; + +/** + * Specify every reverse proxy IP address in your environment. + * This setting is required if $settings['reverse_proxy'] is TRUE. + */ +# $settings['reverse_proxy_addresses'] = ['a.b.c.d', ...]; + +/** + * Reverse proxy trusted headers. + * + * Sets which headers to trust from your reverse proxy. + * + * Common values are: + * - \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_ALL + * - \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED + * + * Note the default value of + * @code + * \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_ALL | \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED + * @endcode + * is not secure by default. The value should be set to only the specific + * headers the reverse proxy uses. For example: + * @code + * \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_ALL + * @endcode + * This would trust the following headers: + * - X_FORWARDED_FOR + * - X_FORWARDED_HOST + * - X_FORWARDED_PROTO + * - X_FORWARDED_PORT + * + * @see \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_ALL + * @see \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED + * @see \Symfony\Component\HttpFoundation\Request::setTrustedProxies + */ +# $settings['reverse_proxy_trusted_headers'] = \Symfony\Component\HttpFoundation\Request::HEADER_X_FORWARDED_ALL | \Symfony\Component\HttpFoundation\Request::HEADER_FORWARDED; + + +/** + * Page caching: + * + * By default, Drupal sends a "Vary: Cookie" HTTP header for anonymous page + * views. This tells a HTTP proxy that it may return a page from its local + * cache without contacting the web server, if the user sends the same Cookie + * header as the user who originally requested the cached page. Without "Vary: + * Cookie", authenticated users would also be served the anonymous page from + * the cache. If the site has mostly anonymous users except a few known + * editors/administrators, the Vary header can be omitted. This allows for + * better caching in HTTP proxies (including reverse proxies), i.e. even if + * clients send different cookies, they still get content served from the cache. + * However, authenticated users should access the site directly (i.e. not use an + * HTTP proxy, and bypass the reverse proxy if one is used) in order to avoid + * getting cached pages from the proxy. + */ +# $settings['omit_vary_cookie'] = TRUE; + + +/** + * Cache TTL for client error (4xx) responses. + * + * Items cached per-URL tend to result in a large number of cache items, and + * this can be problematic on 404 pages which by their nature are unbounded. A + * fixed TTL can be set for these items, defaulting to one hour, so that cache + * backends which do not support LRU can purge older entries. To disable caching + * of client error responses set the value to 0. Currently applies only to + * page_cache module. + */ +# $settings['cache_ttl_4xx'] = 3600; + +/** + * Expiration of cached forms. + * + * Drupal's Form API stores details of forms in a cache and these entries are + * kept for at least 6 hours by default. Expired entries are cleared by cron. + * + * @see \Drupal\Core\Form\FormCache::setCache() + */ +# $settings['form_cache_expiration'] = 21600; + +/** + * Class Loader. + * + * If the APC extension is detected, the Symfony APC class loader is used for + * performance reasons. Detection can be prevented by setting + * class_loader_auto_detect to false, as in the example below. + */ +# $settings['class_loader_auto_detect'] = FALSE; + +/* + * If the APC extension is not detected, either because APC is missing or + * because auto-detection has been disabled, auto-loading falls back to + * Composer's ClassLoader, which is good for development as it does not break + * when code is moved in the file system. You can also decorate the base class + * loader with another cached solution than the Symfony APC class loader, as + * all production sites should have a cached class loader of some sort enabled. + * + * To do so, you may decorate and replace the local $class_loader variable. For + * example, to use Symfony's APC class loader without automatic detection, + * uncomment the code below. + */ +/* +if ($settings['hash_salt']) { + $prefix = 'drupal.' . hash('sha256', 'drupal.' . $settings['hash_salt']); + $apc_loader = new \Symfony\Component\ClassLoader\ApcClassLoader($prefix, $class_loader); + unset($prefix); + $class_loader->unregister(); + $apc_loader->register(); + $class_loader = $apc_loader; +} +*/ + +/** + * Authorized file system operations: + * + * The Update Manager module included with Drupal provides a mechanism for + * site administrators to securely install missing updates for the site + * directly through the web user interface. On securely-configured servers, + * the Update manager will require the administrator to provide SSH or FTP + * credentials before allowing the installation to proceed; this allows the + * site to update the new files as the user who owns all the Drupal files, + * instead of as the user the webserver is running as. On servers where the + * webserver user is itself the owner of the Drupal files, the administrator + * will not be prompted for SSH or FTP credentials (note that these server + * setups are common on shared hosting, but are inherently insecure). + * + * Some sites might wish to disable the above functionality, and only update + * the code directly via SSH or FTP themselves. This setting completely + * disables all functionality related to these authorized file operations. + * + * @see https://www.drupal.org/node/244924 + * + * Remove the leading hash signs to disable. + */ +# $settings['allow_authorize_operations'] = FALSE; + +/** + * Default mode for directories and files written by Drupal. + * + * Value should be in PHP Octal Notation, with leading zero. + */ +# $settings['file_chmod_directory'] = 0775; +# $settings['file_chmod_file'] = 0664; + +/** + * Public file base URL: + * + * An alternative base URL to be used for serving public files. This must + * include any leading directory path. + * + * A different value from the domain used by Drupal to be used for accessing + * public files. This can be used for a simple CDN integration, or to improve + * security by serving user-uploaded files from a different domain or subdomain + * pointing to the same server. Do not include a trailing slash. + */ +# $settings['file_public_base_url'] = 'http://downloads.example.com/files'; + +/** + * Public file path: + * + * A local file system path where public files will be stored. This directory + * must exist and be writable by Drupal. This directory must be relative to + * the Drupal installation directory and be accessible over the web. + */ +# $settings['file_public_path'] = 'sites/default/files'; + +/** + * Private file path: + * + * A local file system path where private files will be stored. This directory + * must be absolute, outside of the Drupal installation directory and not + * accessible over the web. + * + * Note: Caches need to be cleared when this value is changed to make the + * private:// stream wrapper available to the system. + * + * See https://www.drupal.org/documentation/modules/file for more information + * about securing private files. + */ +# $settings['file_private_path'] = ''; + +/** + * Temporary file path: + * + * A local file system path where temporary files will be stored. This directory + * must be absolute, outside of the Drupal installation directory and not + * accessible over the web. + * + * If this is not set, the default for the operating system will be used. + * + * @see \Drupal\Component\FileSystem\FileSystem::getOsTemporaryDirectory() + */ +# $settings['file_temp_path'] = '/tmp'; + +/** + * Session write interval: + * + * Set the minimum interval between each session write to database. + * For performance reasons it defaults to 180. + */ +# $settings['session_write_interval'] = 180; + +/** + * String overrides: + * + * To override specific strings on your site with or without enabling the Locale + * module, add an entry to this list. This functionality allows you to change + * a small number of your site's default English language interface strings. + * + * Remove the leading hash signs to enable. + * + * The "en" part of the variable name, is dynamic and can be any langcode of + * any added language. (eg locale_custom_strings_de for german). + */ +# $settings['locale_custom_strings_en'][''] = [ +# 'forum' => 'Discussion board', +# '@count min' => '@count minutes', +# ]; + +/** + * A custom theme for the offline page: + * + * This applies when the site is explicitly set to maintenance mode through the + * administration page or when the database is inactive due to an error. + * The template file should also be copied into the theme. It is located inside + * 'core/modules/system/templates/maintenance-page.html.twig'. + * + * Note: This setting does not apply to installation and update pages. + */ +# $settings['maintenance_theme'] = 'bartik'; + +/** + * PHP settings: + * + * To see what PHP settings are possible, including whether they can be set at + * runtime (by using ini_set()), read the PHP documentation: + * http://php.net/manual/ini.list.php + * See \Drupal\Core\DrupalKernel::bootEnvironment() for required runtime + * settings and the .htaccess file for non-runtime settings. + * Settings defined there should not be duplicated here so as to avoid conflict + * issues. + */ + +/** + * If you encounter a situation where users post a large amount of text, and + * the result is stripped out upon viewing but can still be edited, Drupal's + * output filter may not have sufficient memory to process it. If you + * experience this issue, you may wish to uncomment the following two lines + * and increase the limits of these variables. For more information, see + * http://php.net/manual/pcre.configuration.php. + */ +# ini_set('pcre.backtrack_limit', 200000); +# ini_set('pcre.recursion_limit', 200000); + +/** + * Configuration overrides. + * + * To globally override specific configuration values for this site, + * set them here. You usually don't need to use this feature. This is + * useful in a configuration file for a vhost or directory, rather than + * the default settings.php. + * + * Note that any values you provide in these variable overrides will not be + * viewable from the Drupal administration interface. The administration + * interface displays the values stored in configuration so that you can stage + * changes to other environments that don't have the overrides. + * + * There are particular configuration values that are risky to override. For + * example, overriding the list of installed modules in 'core.extension' is not + * supported as module install or uninstall has not occurred. Other examples + * include field storage configuration, because it has effects on database + * structure, and 'core.menu.static_menu_link_overrides' since this is cached in + * a way that is not config override aware. Also, note that changing + * configuration values in settings.php will not fire any of the configuration + * change events. + */ +# $config['system.site']['name'] = 'My Drupal site'; +# $config['user.settings']['anonymous'] = 'Visitor'; + +/** + * Fast 404 pages: + * + * Drupal can generate fully themed 404 pages. However, some of these responses + * are for images or other resource files that are not displayed to the user. + * This can waste bandwidth, and also generate server load. + * + * The options below return a simple, fast 404 page for URLs matching a + * specific pattern: + * - $config['system.performance']['fast_404']['exclude_paths']: A regular + * expression to match paths to exclude, such as images generated by image + * styles, or dynamically-resized images. The default pattern provided below + * also excludes the private file system. If you need to add more paths, you + * can add '|path' to the expression. + * - $config['system.performance']['fast_404']['paths']: A regular expression to + * match paths that should return a simple 404 page, rather than the fully + * themed 404 page. If you don't have any aliases ending in htm or html you + * can add '|s?html?' to the expression. + * - $config['system.performance']['fast_404']['html']: The html to return for + * simple 404 pages. + * + * Remove the leading hash signs if you would like to alter this functionality. + */ +# $config['system.performance']['fast_404']['exclude_paths'] = '/\/(?:styles)|(?:system\/files)\//'; +# $config['system.performance']['fast_404']['paths'] = '/\.(?:txt|png|gif|jpe?g|css|js|ico|swf|flv|cgi|bat|pl|dll|exe|asp)$/i'; +# $config['system.performance']['fast_404']['html'] = '404 Not Found

    Not Found

    The requested URL "@path" was not found on this server.

    '; + +/** + * Load services definition file. + */ +$settings['container_yamls'][] = $app_root . '/' . $site_path . '/services.yml'; + +/** + * Override the default service container class. + * + * This is useful for example to trace the service container for performance + * tracking purposes, for testing a service container with an error condition or + * to test a service container that throws an exception. + */ +# $settings['container_base_class'] = '\Drupal\Core\DependencyInjection\Container'; + +/** + * Override the default yaml parser class. + * + * Provide a fully qualified class name here if you would like to provide an + * alternate implementation YAML parser. The class must implement the + * \Drupal\Component\Serialization\SerializationInterface interface. + */ +# $settings['yaml_parser_class'] = NULL; + +/** + * Trusted host configuration. + * + * Drupal core can use the Symfony trusted host mechanism to prevent HTTP Host + * header spoofing. + * + * To enable the trusted host mechanism, you enable your allowable hosts + * in $settings['trusted_host_patterns']. This should be an array of regular + * expression patterns, without delimiters, representing the hosts you would + * like to allow. + * + * For example: + * @code + * $settings['trusted_host_patterns'] = [ + * '^www\.example\.com$', + * ]; + * @endcode + * will allow the site to only run from www.example.com. + * + * If you are running multisite, or if you are running your site from + * different domain names (eg, you don't redirect http://www.example.com to + * http://example.com), you should specify all of the host patterns that are + * allowed by your site. + * + * For example: + * @code + * $settings['trusted_host_patterns'] = [ + * '^example\.com$', + * '^.+\.example\.com$', + * '^example\.org$', + * '^.+\.example\.org$', + * ]; + * @endcode + * will allow the site to run off of all variants of example.com and + * example.org, with all subdomains included. + */ + +/** + * The default list of directories that will be ignored by Drupal's file API. + * + * By default ignore node_modules and bower_components folders to avoid issues + * with common frontend tools and recursive scanning of directories looking for + * extensions. + * + * @see \Drupal\Core\File\FileSystemInterface::scanDirectory() + * @see \Drupal\Core\Extension\ExtensionDiscovery::scanDirectory() + */ +$settings['file_scan_ignore_directories'] = [ + 'node_modules', + 'bower_components', +]; + +/** + * The default number of entities to update in a batch process. + * + * This is used by update and post-update functions that need to go through and + * change all the entities on a site, so it is useful to increase this number + * if your hosting configuration (i.e. RAM allocation, CPU speed) allows for a + * larger number of entities to be processed in a single batch run. + */ +$settings['entity_update_batch_size'] = 50; + +/** + * Entity update backup. + * + * This is used to inform the entity storage handler that the backup tables as + * well as the original entity type and field storage definitions should be + * retained after a successful entity update process. + */ +$settings['entity_update_backup'] = TRUE; + +/** + * Load isle override configuration, if available. + */ +if (file_exists($app_root . '/' . $site_path . '/settings.project.php')) { + include $app_root . '/' . $site_path . '/settings.project.php'; +} + +/** + * Load local development override configuration, if available. + * + * Use settings.local.php to override variables on secondary (staging, + * development, etc) installations of this site. Typically used to disable + * caching, JavaScript/CSS compression, re-routing of outgoing emails, and + * other things that should not happen on development and testing sites. + * + * Keep this code block at the end of this file to take full effect. + */ +# +# if (file_exists($app_root . '/' . $site_path . '/settings.local.php')) { +# include $app_root . '/' . $site_path . '/settings.local.php'; +# } diff --git a/config/drupal/settings.isle.php b/config/drupal/settings.project.php similarity index 100% rename from config/drupal/settings.isle.php rename to config/drupal/settings.project.php diff --git a/config/drupal/snippet.txt b/config/drupal/snippet.txt deleted file mode 100644 index 0f1282a7a..000000000 --- a/config/drupal/snippet.txt +++ /dev/null @@ -1,7 +0,0 @@ - -/** - * Load isle override configuration, if available. - */ -if (file_exists($app_root . '/' . $site_path . '/settings.isle.php')) { - include $app_root . '/' . $site_path . '/settings.isle.php'; -} From 1da4ce94f86a8912d9d5b39fe64d33eabedcd299 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Sat, 28 Mar 2020 15:14:18 -0400 Subject: [PATCH 100/107] Instead of using sed for settings.php customization we use the pre-configured one. --- scripts/drupal/init.sh | 43 +++++++++++++----------------------------- 1 file changed, 13 insertions(+), 30 deletions(-) diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 97c0cd075..34032b2e4 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -96,40 +96,23 @@ function create_required_files() { local drupal_root="$current_folder/codebase/web" local default_dir="$drupal_root/sites/default" local settings="${default_dir}/settings.php" - local settings_default="${default_dir}/default.settings.php" - local settings_isle="settings.isle.php" - local insert_after="\$settings\[\'entity_update_backup\'\] \= TRUE\;" - local config_sync_pattern="\$settings\['config_sync_directory'] = '\/directory\/outside\/webroot'" - local snippet="${config_dir}/snippet.txt" - - # Prepare the settings file for installation. In case the user has an existing - # one we skip overriding it. - if [[ ! -f "${settings}" && -f "${settings_default}" ]]; then - cp ${settings_default} ${settings} - fi - - # Ensuring that settings.php is pointing to the proper config_sync_directory. - if [[ $(grep "^#.* ${config_sync_pattern}" ${settings} || true) ]]; then - local replace="\$settings\['config_sync_directory'] = '..\/config\/sync'" - if $is_darwin; then - sed -i '' -e "s/^#.* ${config_sync_pattern}/${replace}/" ${settings} - else - sed -i -e "s/^#.* ${config_sync_pattern}/${replace}/" ${settings} - fi - fi + local settings_project="settings.project.php" # Insert settings.isle.php snippet into the settings.php file - if [[ ! -f "${default_dir}/${settings_isle}" ]]; then - echo -e "\033[1m[INFO]\033[0m Setting up settings.isle.php to be included in settings.php" + if [[ ! -f "${default_dir}/${settings_project}" ]]; then echo " " - if $is_darwin; then - sed -i '' -e "/${insert_after}/r ${snippet}" ${settings} - else - sed -i -e "/${insert_after}/r ${snippet}" ${settings} + echo -e "\033[1m[INFO]\033[0m Adjusting settings.php to work with the Isle dc." + echo " If there is any customization made to the current settings.php, they will need to be moved manually." + + # Let make a backup of an existing settings.php if found + if [[ -f "${settings}" ]]; then + mv "${settings}" "${settings}.bak" + echo " An existing settings.php was found and it was renamed to ${settings}.bak." fi - cp "${config_dir}/${settings_isle}" "${default_dir}/${settings_isle}" - echo " settings.isle.php was successfully setup." - echo >&2 + # Copying the settings with Isle customization in place. + cp "${config_dir}/settings.php" "${settings}" + cp "${config_dir}/${settings_project}" "${default_dir}/${settings_project}" + echo " " fi } From 3ee03e8377e7a8af082633ce86a68554475237ba Mon Sep 17 00:00:00 2001 From: Aaron Birkland Date: Wed, 1 Apr 2020 08:22:21 -0400 Subject: [PATCH 101/107] Fix paths on Windows + git bash When using Docker in Windows with MSYS-based bash and tooling (e.g. bash and unix tools installed with git for Windows), certain paths are mangled unless MSYS_NO_PATHCONV=1 is enabled. --- Makefile | 4 ++-- README.md | 1 + scripts/drupal/init.sh | 2 +- scripts/solr/create-core.sh | 4 +++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 63511353d..fc7dfdb9c 100644 --- a/Makefile +++ b/Makefile @@ -18,10 +18,10 @@ solr_init: ./scripts/solr/create-core.sh up: - docker-compose -p $(docker_compose_project) up --remove-orphans --detach + MSYS_NO_PATHCONV=1 docker-compose -p $(docker_compose_project) up --remove-orphans --detach build: - docker-compose -p $(docker_compose_project) up \ + MSYS_NO_PATHCONV=1 docker-compose -p $(docker_compose_project) up \ --build \ --detach \ --remove-orphans diff --git a/README.md b/README.md index dc9611930..c32ec3974 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Currently there are blank `.keep` files in most of the `config/service` director * Desktop / laptop / VM * Docker-CE 19.x+ + * If using Docker Desktop for Windows, any stable release *after* 2.2.0.4, or use a 2.2.0.4 with a [patch](https://download-stage.docker.com/win/stable/43542/Docker%20Desktop%20Installer.exe) due to a [bug](https://github.com/docker/for-win/issues/6016) * Docker-compose version 1.25.x+ * Git 2.0+ diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index 34032b2e4..fad318213 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -151,7 +151,7 @@ if [[ ! $composer ]]; then if $is_darwin; then composer="docker container run -it --rm --user $UID:$GUID -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" else - composer="docker container run -t --rm --user $UID:$GUID -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" + composer="env MSYS_NO_PATHCONV=1 docker container run -t --rm --user $UID:$GUID -v $HOME/.composer:/tmp -v $PWD:/app composer:1.9.3" fi fi diff --git a/scripts/solr/create-core.sh b/scripts/solr/create-core.sh index d0348519e..7cdb9b28f 100755 --- a/scripts/solr/create-core.sh +++ b/scripts/solr/create-core.sh @@ -1 +1,3 @@ -docker-compose -p islandora exec solr /opt/solr/bin/solr create -c islandora +#!/bin/bash + +env MSYS_NO_PATHCONV=1 docker-compose -p islandora exec -T solr /opt/solr/bin/solr create -c islandora From d410bccb1e5b7a408f5d84a350fbc9130797f815 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Sun, 12 Apr 2020 18:21:39 -0400 Subject: [PATCH 102/107] Set timezone to UTC, switch default workdir to docroot, change mariadb service name to drupal_db. --- docker-compose.yml | 87 ++++++++++++++++++++++------------------------ drupal.Dockerfile | 1 + 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index aad7d657f..2225b26cf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ version: "2.4" ## MVP 2 - Microservices & Connectors (Houdini, Homarus, Hypercube, FITS, ActiveMQ and Cantaloupe) services: - mariadb: + drupal_db: image: mariadb:10.3.22 environment: MYSQL_DATABASE: ${DB_NAME:-drupal} @@ -42,7 +42,7 @@ services: - NGINX_SERVER_ROOT=${APP_DOCROOT:-/var/www/app/web} - NGINX_LISTEN_PORT=${NGINX_LISTEN_PORT:-8080} depends_on: - mariadb: + drupal_db: condition: service_healthy env_file: - php.env @@ -54,10 +54,10 @@ services: DB_NAME: ${DB_NAME:-drupal} DB_USER: ${DB_USER:-drupal} DB_PASSWORD: ${DB_PASSWORD:-dbpassword} - DB_HOST: "mariadb" + DB_HOST: "drupal_db" DB_PORT: ${DB_PORT:-3306} DB_DRIVER: ${DB_DRIVER:-mysql} - DRUSH_OPTIONS_URI: ${DRUPAL_LOCAL_URL:-islandora.localhost} + DRUSH_OPTIONS_URI: "${PROJECT_BASE_URL:-idcp.localhost}:8000" DRUPAL_HASH_SALT: ${DRUPAL_HASH_SALT:-tfvQNpDFG2CjY9WHGNgFqC3eoMjyg5pZdGMQ74zjmnIoe0bi8F3hUvBWnGFIAM3nnj2iWA} NGINX_LISTEN_PORT: "8080" AUTO_INSTALL: "true" @@ -74,7 +74,7 @@ services: - ${APP_DOCROOT:-/var/www/app/web}/profiles/contrib - ${APP_DOCROOT:-/var/www/app/web}/libraries labels: - - "traefik.http.routers.${APP_NAME:-islandora}.rule=Host(`${DRUPAL_LOCAL_URL:-islandora.localhost}`)" + - "traefik.http.routers.${APP_NAME:-islandora}.rule=Host(`${PROJECT_BASE_URL:-idcp.localhost}`)" - "traefik.http.services.${APP_NAME:-islandora}.loadbalancer.server.port=8080" solr: @@ -107,58 +107,56 @@ services: #labels: # - "traefik.http.routers.${PROJECT_NAME}_cantaloupe.rule=Host(`cantaloupe.${PROJECT_BASE_URL}`)" -### MVP2 - Microservices - Start + ### MVP2 - Microservices - Start -## TO DO: Determine where poppler-utils for generating PDFs is to be located? + ## TO DO: Determine where poppler-utils for generating PDFs is to be located? houdini: image: borndigital/isle-houdini:mvp2-alpha -# build: -# context: https://github.com/Islandora-Devops/isle-houdini.git#dev -# args: -# HOUDINI_JWT_ADMIN_TOKEN: $HOUDINI_JWT_ADMIN_TOKEN -# HOUDINI_LOG_LEVEL: $HOUDINI_LOG_LEVEL + # build: + # context: https://github.com/Islandora-Devops/isle-houdini.git#dev + # args: + # HOUDINI_JWT_ADMIN_TOKEN: $HOUDINI_JWT_ADMIN_TOKEN + # HOUDINI_LOG_LEVEL: $HOUDINI_LOG_LEVEL container_name: "${PROJECT_NAME}_houdini" # networks: # - internal -# ports: -# - "8000:8000" + # ports: + # - "8000:8000" volumes: - ./jwt:/opt/jwt - ./config/crayfish/php.ini:/usr/local/etc/php/php.ini #labels: # - "traefik.http.routers.${PROJECT_NAME}_houdini.rule=Host(`houdini.${PROJECT_BASE_URL}`)" - homarus: image: borndigital/isle-homarus:mvp2-alpha -# build: -# context: https://github.com/Islandora-Devops/isle-homarus.git#dev -# args: -# HOMARUS_JWT_ADMIN_TOKEN: $HOMARUS_JWT_ADMIN_TOKEN -# HOMARUS_LOG_LEVEL: $HOMARUS_LOG_LEVEL + # build: + # context: https://github.com/Islandora-Devops/isle-homarus.git#dev + # args: + # HOMARUS_JWT_ADMIN_TOKEN: $HOMARUS_JWT_ADMIN_TOKEN + # HOMARUS_LOG_LEVEL: $HOMARUS_LOG_LEVEL container_name: "${PROJECT_NAME}_homarus" -# ports: -# - "8001:8000" + # ports: + # - "8001:8000" volumes: - ./jwt:/opt/jwt - ./config/crayfish/php.ini:/usr/local/etc/php/php.ini #labels: # - "traefik.http.routers.${PROJECT_NAME}_homarus.rule=Host(`homarus.${PROJECT_BASE_URL}`)" - hypercube: image: borndigital/isle-hypercube:mvp2-alpha -# build: -# context: https://github.com/Islandora-Devops/isle-hypercube.git#dev -# args: -# HYPERCUBE_JWT_ADMIN_TOKEN: $HYPERCUBE_JWT_ADMIN_TOKEN -# HYPERCUBE_LOG_LEVEL: $HYPERCUBE_LOG_LEVEL + # build: + # context: https://github.com/Islandora-Devops/isle-hypercube.git#dev + # args: + # HYPERCUBE_JWT_ADMIN_TOKEN: $HYPERCUBE_JWT_ADMIN_TOKEN + # HYPERCUBE_LOG_LEVEL: $HYPERCUBE_LOG_LEVEL container_name: "${PROJECT_NAME}_hypercube" # networks: # - internal -# ports: -# - "8002:8000" + # ports: + # - "8002:8000" volumes: - ./jwt:/opt/jwt - ./config/crayfish/php.ini:/usr/local/etc/php/php.ini @@ -167,15 +165,15 @@ services: crayfits: image: borndigital/isle-crayfits:mvp2-alpha -# build: -# context: https://github.com/Islandora-Devops/isle-crayfits.git#dev -# args: -# FITS_WEBSERVICE_URI: http://fits:8080/fits/examine + # build: + # context: https://github.com/Islandora-Devops/isle-crayfits.git#dev + # args: + # FITS_WEBSERVICE_URI: http://fits:8080/fits/examine container_name: "${PROJECT_NAME}_crayfits" # networks: # - internal -# ports: -# - "8003:8000" + # ports: + # - "8003:8000" volumes: - ./config/crayfish/php.ini:/usr/local/etc/php/php.ini #labels: @@ -186,14 +184,14 @@ services: container_name: "${PROJECT_NAME}_fits" # networks: # - internal -# ports: -# - '8085:8080' - # TO DO: Determine what type of container handling is needed, exposed ports etc? - #volumes: - #labels: - # - "traefik.http.routers.${PROJECT_NAME}_fits.rule=Host(`fits.${PROJECT_BASE_URL}`)" + # ports: + # - '8085:8080' + # TO DO: Determine what type of container handling is needed, exposed ports etc? + #volumes: + #labels: + # - "traefik.http.routers.${PROJECT_NAME}_fits.rule=Host(`fits.${PROJECT_BASE_URL}`)" -### MVP2 - Microservices - End + ### MVP2 - Microservices - End traefik: # review https://hub.docker.com/_/traefik @@ -229,13 +227,12 @@ services: container_name: "${PROJECT_NAME}_activemq" env_file: .env ports: - - "8161:8161" # To make mgmt console available for dev purposes. use admin:admin to log in + - "8161:8161" # To make mgmt console available for dev purposes. use admin:admin to log in # networks: # - internal volumes: - activemq-data:/opt/activemq/data - # networks: # internal: # external: diff --git a/drupal.Dockerfile b/drupal.Dockerfile index 700a8b4cd..33eb215ea 100644 --- a/drupal.Dockerfile +++ b/drupal.Dockerfile @@ -136,6 +136,7 @@ COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/w COPY --from=composer-build --chown=${APP_RUNNER_USER}:${APP_RUNNER_GROUP} /app/drush/contrib ./drush/contrib # If stage 2 available and generated js and css artifacts files, they can also be copied inside this folder. +WORKDIR ${APP_DOCROOT} # # Stage 4: The production setup # From 01841fd0409a183373bc389ff39ba38a56554e74 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Sun, 12 Apr 2020 19:35:27 -0400 Subject: [PATCH 103/107] Initial implementation for the sample.env and adjusted drupal db and drupal service accordingly. --- docker-compose.yml | 33 +++++++++++++-------------------- php.env | 16 ---------------- sample.env | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 36 deletions(-) delete mode 100644 php.env create mode 100644 sample.env diff --git a/docker-compose.yml b/docker-compose.yml index 2225b26cf..deb385817 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,8 +9,8 @@ services: drupal_db: image: mariadb:10.3.22 environment: - MYSQL_DATABASE: ${DB_NAME:-drupal} - MYSQL_USER: ${DB_USER:-drupal} + MYSQL_DATABASE: ${DB_NAME:-islandora} + MYSQL_USER: ${DB_USER:-islandora} MYSQL_PASSWORD: ${DB_PASSWORD:-dbpassword} MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-dbpassword} volumes: @@ -28,26 +28,21 @@ services: - code_dir=./codebase - base_image_tag=7.2.28-1.17.8-0ceedc1b - build_environment=dev + - PHP_DATE_TIMEZONE=${PHP_DATE_TIMEZONE:-UTC} # XDEBUG confd build time args - you can set it to 0 turn it off. - PHP_XDEBUG=${PHP_XDEBUG:-1} - - PHP_IDE_CONFIG="serverName=${PHP_IDE_CONFIG_SERVER_NAME:-islandora.localhost}" + - PHP_IDE_CONFIG="serverName=${PHP_IDE_CONFIG_SERVER_NAME:-idcp.localhost}" - PHP_XDEBUG_REMOTE_HOST=${PHP_XDEBUG_REMOTE_HOST:-host.docker.internal} - #-PHP_XDEBUG_REMOTE_HOST: host.docker.internal # Docker 18.03+ Mac/Win - #-PHP_XDEBUG_REMOTE_HOST: 172.17.0.1 # Linux - #-PHP_XDEBUG_REMOTE_HOST: 10.254.254.254 # macOS, Docker < 18.03 - #-PHP_XDEBUG_REMOTE_HOST: 10.0.75.1 # Windows, Docker < 18.03 - PHP_XDEBUG_DEFAULT_ENABLE=${PHP_XDEBUG_DEFAULT_ENABLE:-1} - PHP_XDEBUG_REMOTE_CONNECT_BACK=${PHP_XDEBUG_REMOTE_CONNECT_BACK:-0} # NGINX confd build time args - NGINX_SERVER_ROOT=${APP_DOCROOT:-/var/www/app/web} - - NGINX_LISTEN_PORT=${NGINX_LISTEN_PORT:-8080} + - NGINX_LISTEN_PORT="8080" depends_on: drupal_db: condition: service_healthy - env_file: - - php.env environment: - APP_NAME: ${APP_NAME:-islandora} + APP_NAME: ${DRUPAL_SITE_NAME:-islandora} APP_ROOT: ${APP_ROOT:-/var/www/app} APP_ACCOUNT_NAME: ${DRUPAL_USER_NAME:-islandora} APP_ACCOUNT_MAIL: ${DRUPAL_USER_EMAIL:-islandora@example.com} @@ -55,12 +50,12 @@ services: DB_USER: ${DB_USER:-drupal} DB_PASSWORD: ${DB_PASSWORD:-dbpassword} DB_HOST: "drupal_db" - DB_PORT: ${DB_PORT:-3306} - DB_DRIVER: ${DB_DRIVER:-mysql} + DB_PORT: "3306" + DB_DRIVER: "mysql" DRUSH_OPTIONS_URI: "${PROJECT_BASE_URL:-idcp.localhost}:8000" DRUPAL_HASH_SALT: ${DRUPAL_HASH_SALT:-tfvQNpDFG2CjY9WHGNgFqC3eoMjyg5pZdGMQ74zjmnIoe0bi8F3hUvBWnGFIAM3nnj2iWA} NGINX_LISTEN_PORT: "8080" - AUTO_INSTALL: "true" + AUTO_INSTALL: ${DRUPAL_AUTO_INSTALL:-true} volumes: # set delegated mode here on docker for mac for faster disk I/O - ./codebase:${APP_ROOT:-/var/www/app}:delegated @@ -74,17 +69,15 @@ services: - ${APP_DOCROOT:-/var/www/app/web}/profiles/contrib - ${APP_DOCROOT:-/var/www/app/web}/libraries labels: - - "traefik.http.routers.${APP_NAME:-islandora}.rule=Host(`${PROJECT_BASE_URL:-idcp.localhost}`)" - - "traefik.http.services.${APP_NAME:-islandora}.loadbalancer.server.port=8080" + - "traefik.http.routers.${PROJECT_NAME:-islandora}.rule=Host(`${PROJECT_BASE_URL:-idcp.localhost}`)" + - "traefik.http.services.${PROJECT_NAME:-islandora}.loadbalancer.server.port=8080" solr: image: wodby/solr:$SOLR_TAG container_name: "${PROJECT_NAME}_solr" - # networks: - # - internal environment: - SOLR_DEFAULT_CONFIG_SET: $SOLR_CONFIG_SET - SOLR_HEAP: 1024m + SOLR_DEFAULT_CONFIG_SET: ${SOLR_CONFIG_SET:-search_api_solr_8.x-3.2} + SOLR_HEAP: ${SOLR_HEAP:-1024m} volumes: - solr-data:/opt/solr/server/solr # TO DO: Determine what type of container handling is needed diff --git a/php.env b/php.env deleted file mode 100644 index f7c558b4a..000000000 --- a/php.env +++ /dev/null @@ -1,16 +0,0 @@ -## ISLE 8 Draft Prototype -## Feb 2020 - MVP 1 -## Release (Alpha) (0.0.1) -## PHP ENVs in addition to .env globals - -### --- DRUPAL ---- - -DRUPAL_INSTALL_TYPE=standard -DRUPAL_LOCALE=us -DRUPAL_LANGUAGE=en -DRUPAL_USER=islandora -DRUPAL_SITE_NAME=ISLE 8 Local -DRUPAL_SITE_MAIL=admin@example.com -DRUPAL_USER_PASSWORD=islandora -DRUPAL_USER_EMAIL=islandora@example.com -DRUPAL_LOCAL_URL=islandora.localhost diff --git a/sample.env b/sample.env new file mode 100644 index 000000000..c68271b80 --- /dev/null +++ b/sample.env @@ -0,0 +1,44 @@ +## ISLE 8 Draft Prototype + +## --- GENERAL PROJECT SETTINGS --- + +PROJECT_NAME=isle_dc_proto +PROJECT_BASE_URL=idcp.localhost +PROJECT_SHORT_ID=idcp + +## --- DRUPAL ---- + +APP_DOCROOT=/var/www/app/web +DRUPAL_AUTO_INSTALL=true +DRUPAL_USER_NAME=islandora +DRUPAL_SITE_NAME=ISLE 8 Local +DRUPAL_SITE_MAIL=islandora@example.com +DRUPAL_USER_PASSWORD=islandora +DRUPAL_USER_EMAIL=islandora@example.com +DRUPAL_HASH_SALT=tfvQNpDFG2CjY9WHGNgFqC3eoMjyg5pZdGMQ74zjmnIoe0bi8F3hUvBWnGFIAM3nnj2iWA + +### --- DATABASE --- + +DB_NAME=islandora +DB_USER=islandora +DB_PASSWORD=dbpassword +DB_ROOT_PASSWORD=dbpassword + +### PHP + +PHP_DATE_TIMEZONE=UTC +PHP_XDEBUG=1 +PHP_IDE_CONFIG_SERVER_NAME=idcp.localhost +# PHP_XDEBUG_REMOTE_HOST=host.docker.internal # Docker 18.03+ Mac/Win +# PHP_XDEBUG_REMOTE_HOST=172.17.0.1 # Linux +# PHP_XDEBUG_REMOTE_HOST=10.254.254.254 # macOS, Docker < 18.03 +# PHP_XDEBUG_REMOTE_HOST=10.0.75.1 # Windows, Docker < 18.03 +PHP_XDEBUG_REMOTE_HOST=host.docker.internal +PHP_XDEBUG_DEFAULT_ENABLE=1 +PHP_XDEBUG_REMOTE_CONNECT_BACK=0 + +## --- SOLR ---- + +SOLR_TAG=8-4.1.2 +SOLR_CONFIG_SET="search_api_solr_8.x-3.2" +SOLR_HEAP=1024m From c7cefabcdb69e39d0bb89b19534e9cc663c1e104 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Mon, 13 Apr 2020 10:43:45 -0400 Subject: [PATCH 104/107] removed quotation on the nginx port. --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index deb385817..5f2ffd2dc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -37,7 +37,7 @@ services: - PHP_XDEBUG_REMOTE_CONNECT_BACK=${PHP_XDEBUG_REMOTE_CONNECT_BACK:-0} # NGINX confd build time args - NGINX_SERVER_ROOT=${APP_DOCROOT:-/var/www/app/web} - - NGINX_LISTEN_PORT="8080" + - NGINX_LISTEN_PORT=8080 depends_on: drupal_db: condition: service_healthy @@ -54,7 +54,7 @@ services: DB_DRIVER: "mysql" DRUSH_OPTIONS_URI: "${PROJECT_BASE_URL:-idcp.localhost}:8000" DRUPAL_HASH_SALT: ${DRUPAL_HASH_SALT:-tfvQNpDFG2CjY9WHGNgFqC3eoMjyg5pZdGMQ74zjmnIoe0bi8F3hUvBWnGFIAM3nnj2iWA} - NGINX_LISTEN_PORT: "8080" + NGINX_LISTEN_PORT: 8080 AUTO_INSTALL: ${DRUPAL_AUTO_INSTALL:-true} volumes: # set delegated mode here on docker for mac for faster disk I/O From 1783d45153d70cca7a386b6c3daa26c366fd8009 Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 14 Apr 2020 13:20:20 -0400 Subject: [PATCH 105/107] Adding an local env to overwrite the default dockerfile. --- docker-compose.yml | 2 +- sample.env | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5f2ffd2dc..35ae62c7f 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,7 +22,7 @@ services: drupal: build: context: ./ - dockerfile: ./drupal.Dockerfile + dockerfile: ${PROJECT_DRUPAL_DOCKERFILE:-./drupal.Dockerfile} target: dev args: - code_dir=./codebase diff --git a/sample.env b/sample.env index c68271b80..5c0c42935 100644 --- a/sample.env +++ b/sample.env @@ -5,6 +5,8 @@ PROJECT_NAME=isle_dc_proto PROJECT_BASE_URL=idcp.localhost PROJECT_SHORT_ID=idcp +# PROJECT_DRUPAL_DOCKERFILE=./codebase/Dockerfile +PROJECT_DRUPAL_DOCKERFILE=./drupal.Dockerfile ## --- DRUPAL ---- From 055825f1caaae02ab3c46bebb28d23ef1eed4abc Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Tue, 14 Apr 2020 13:32:02 -0400 Subject: [PATCH 106/107] Removed the bind mount of drupal db data on local folder. --- docker-compose.yml | 9 ++------- scripts/drupal/init.sh | 4 ++-- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 35ae62c7f..a7df2a04d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,7 +14,7 @@ services: MYSQL_PASSWORD: ${DB_PASSWORD:-dbpassword} MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-dbpassword} volumes: - - mariadb-data:/var/lib/mysql + - drupal-db-data:/var/lib/mysql healthcheck: # https://dev.mysql.com/doc/refman/8.0/en/mysqladmin.html#command_mysqladmin_ping test: ["CMD", "mysqladmin", "ping", "--silent"] @@ -233,12 +233,7 @@ services: volumes: # For local database storage persistance we bind mount a local folder. # This folder needs to be created prior to docker-compose up or build. - mariadb-data: - driver: local - driver_opts: - type: none - o: bind - device: ${PWD}/data/drupal/database + drupal-db-data: activemq-data: solr-data: # isle-dc-postgres-data # Added to prototype but not currently used diff --git a/scripts/drupal/init.sh b/scripts/drupal/init.sh index fad318213..7941942ac 100755 --- a/scripts/drupal/init.sh +++ b/scripts/drupal/init.sh @@ -170,9 +170,9 @@ if [[ "$codebase" == "islandora" && ! -f codebase/config/sync/core.extension.yml fi ### -# Initialize drupal database and files persistent storage folders. +# Initialize drupal files persistent storage folders. ### -mkdir -p $PWD/data/drupal/files/public $PWD/data/drupal/files/private $PWD/data/drupal/database +mkdir -p $PWD/data/drupal/files/public $PWD/data/drupal/files/private ### # Running composer install just in case the user has an existing project. From 09215843bc79ee8febbd7cfff81fa8ed2a684b1e Mon Sep 17 00:00:00 2001 From: Nia Kathoni Date: Wed, 15 Apr 2020 12:25:19 -0400 Subject: [PATCH 107/107] Revert to mariadb service. --- docker-compose.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index a7df2a04d..8e961edc2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ version: "2.4" ## MVP 2 - Microservices & Connectors (Houdini, Homarus, Hypercube, FITS, ActiveMQ and Cantaloupe) services: - drupal_db: + mariadb: image: mariadb:10.3.22 environment: MYSQL_DATABASE: ${DB_NAME:-islandora} @@ -14,7 +14,7 @@ services: MYSQL_PASSWORD: ${DB_PASSWORD:-dbpassword} MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD:-dbpassword} volumes: - - drupal-db-data:/var/lib/mysql + - mariadb-data:/var/lib/mysql healthcheck: # https://dev.mysql.com/doc/refman/8.0/en/mysqladmin.html#command_mysqladmin_ping test: ["CMD", "mysqladmin", "ping", "--silent"] @@ -49,7 +49,7 @@ services: DB_NAME: ${DB_NAME:-drupal} DB_USER: ${DB_USER:-drupal} DB_PASSWORD: ${DB_PASSWORD:-dbpassword} - DB_HOST: "drupal_db" + DB_HOST: "mariadb" DB_PORT: "3306" DB_DRIVER: "mysql" DRUSH_OPTIONS_URI: "${PROJECT_BASE_URL:-idcp.localhost}:8000" @@ -233,7 +233,7 @@ services: volumes: # For local database storage persistance we bind mount a local folder. # This folder needs to be created prior to docker-compose up or build. - drupal-db-data: + mariadb-data: activemq-data: solr-data: # isle-dc-postgres-data # Added to prototype but not currently used