diff --git a/.docksal/docksal.env b/.docksal/docksal.env index e5720bf37..f5af0d850 100644 --- a/.docksal/docksal.env +++ b/.docksal/docksal.env @@ -1,4 +1,4 @@ DOCKSAL_STACK=default DOCROOT=html DB_IMAGE="docksal/mysql:8.0-2.0" -CLI_IMAGE="docksal/cli:php8.1-3.2" \ No newline at end of file +CLI_IMAGE="docksal/cli:php8.2" \ No newline at end of file diff --git a/.github/tests/docker-compose.yml b/.github/tests/docker-compose.yml index a822a9e8a..3601d2b68 100644 --- a/.github/tests/docker-compose.yml +++ b/.github/tests/docker-compose.yml @@ -22,7 +22,7 @@ services: - default mysql: - image: public.ecr.aws/unocha/mysql:10.6 + image: public.ecr.aws/unocha/mysql:10.11 hostname: ghi-test-mysql container_name: ghi-test-mysql environment: @@ -73,4 +73,4 @@ services: ports: - "8081:80" networks: - - default \ No newline at end of file + - default diff --git a/.github/workflows/composer-update.yml b/.github/workflows/composer-update.yml index 1e7a8c7e4..0d8e1025f 100644 --- a/.github/workflows/composer-update.yml +++ b/.github/workflows/composer-update.yml @@ -3,6 +3,7 @@ name: Run Composer Update on: schedule: - cron: '40 7 * * 4' + workflow_dispatch: jobs: build: diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 6c261ba17..3808ca492 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -1,7 +1,7 @@ name: Run tests on: [pull_request] - + jobs: tests: runs-on: ubuntu-latest @@ -11,19 +11,19 @@ jobs: pull-requests: write actions: read statuses: write - + steps: - name: Checkout Code id: checkout uses: actions/checkout@v3 - + - name: Extract PHP Version id: php uses: docker://ghcr.io/un-ocha/actions:extract-php-version-main with: docker_file: 'docker/Dockerfile' docker_image: 'public.ecr.aws/unocha/php-k8s' - + - name: Setup PHP with PECL extension uses: shivammathur/setup-php@v2 with: diff --git a/docker/Dockerfile b/docker/Dockerfile index 790ee041d..daf249f0a 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,5 @@ # Build the code. -FROM public.ecr.aws/unocha/unified-builder:8.1-stable as builder +FROM public.ecr.aws/unocha/php-k8s:8.2-stable as builder ARG BRANCH_ENVIRONMENT ENV NODE_ENV=$BRANCH_ENVIRONMENT @@ -21,7 +21,7 @@ RUN mkdir -m 0775 -p html/sites/default && \ ################################################################################ # Generate the image. -FROM public.ecr.aws/unocha/php-k8s:8.1-stable +FROM public.ecr.aws/unocha/php-k8s:8.2-stable ARG VCS_REF ARG VCS_URL @@ -58,13 +58,11 @@ COPY --from=builder /srv/www/composer.patches.json /srv/www/composer.patches.jso COPY --from=builder /srv/www/composer.lock /srv/www/composer.lock COPY --from=builder /srv/www/patches /srv/www/patches COPY --from=builder /srv/www/scripts /srv/www/scripts -COPY --from=builder /srv/www/docker/etc/nginx/apps/drupal/fastcgi_drupal.conf /etc/nginx/apps/drupal/fastcgi_drupal.conf -COPY --from=builder /srv/www/docker/etc/nginx/apps/drupal/drupal.conf /etc/nginx/apps/drupal/drupal.conf COPY --from=builder /srv/www/docker/etc/nginx/custom /etc/nginx/custom/ COPY --from=builder /srv/www/docker/etc/nginx/sites-enabled/02_mapbox_proxy_cache.conf /etc/nginx/sites-enabled/02_mapbox_proxy_cache.conf COPY --from=builder /srv/www/docker/99-elastic-apm-custom.ini /tmp/99-elastic-apm-custom.ini -RUN curl -L -o /tmp/apm-agent-php_1.6.1_all.apk https://github.com/elastic/apm-agent-php/releases/download/v1.6.1/apm-agent-php_1.6.1_all.apk && \ - apk add --allow-untrusted /tmp/apm-agent-php_1.6.1_all.apk && \ - rm -f /tmp/apm-agent-php_1.6.1_all.apk && \ - mv -f /tmp/99-elastic-apm-custom.ini /etc/php81/conf.d/99-elastic-apm-custom.ini +RUN curl -L -o /tmp/apm-agent-php_all.apk https://github.com/elastic/apm-agent-php/releases/download/v1.10.0/apm-agent-php_1.10.0_all.apk && \ + apk add --allow-untrusted /tmp/apm-agent-php_all.apk && \ + rm -f /tmp/apm-agent-php_all.apk && \ + mv -f /tmp/99-elastic-apm-custom.ini /etc/php82/conf.d/99-elastic-apm-custom.ini diff --git a/docker/etc/nginx/apps/drupal/drupal.conf b/docker/etc/nginx/apps/drupal/drupal.conf deleted file mode 100644 index 42ca21025..000000000 --- a/docker/etc/nginx/apps/drupal/drupal.conf +++ /dev/null @@ -1,386 +0,0 @@ -# -*- mode: nginx; mode: flyspell-prog; ispell-local-dictionary: "american" -*- -### Nginx configuration for Drupal. This configuration makes use of -### drush (http:///drupal.org/project/drush) for site maintenance -### and like tasks: -### -### 1. Run the cronjobs. -### 2. Run the DB and code updates: drush up or drush upc followed by -### drush updb to run any DB updates required by the code upgrades -### that were performed. -### 3. Disabling of xmlrpc.xml, install.php (needed only for -### installing the site) and update.php: all updates are now -### handled through drush. - -## The 'default' location. -location / { - - # Passworded until launch. Keeps bots out. - # satisfy any; - # allow 10.5.103.1/32; - # allow 10.5.103.2/32; - # allow 10.5.104.1/32; - # allow 10.5.104.2/32; - # deny all; - # auth_basic "Restricted access"; #realm - # auth_basic_user_file /etc/nginx/.htpasswd-users; - - ## Drupal 404 from can impact performance. If using a module like - ## search404 then 404's *have *to be handled by Drupal. Uncomment to - ## relay the handling of 404's to Drupal. - ## error_page 404 /index.php; - - ## Using a nested location is the 'correct' way to use regexes. - - ## Regular private file serving (i.e. handled by Drupal). - location ^~ /system/files/ { - ## Include the specific FastCGI configuration. This is for a - ## FCGI backend like php-cgi or php-fpm. - include apps/drupal/fastcgi_drupal.conf; - fastcgi_pass phpcgi; - - ## If proxying to apache comment the two lines above and - ## uncomment the two lines below. - #proxy_pass http://phpapache/index.php?q=$uri; - #proxy_set_header Connection ''; - - ## For not signaling a 404 in the error log whenever the - ## system/files directory is accessed add the line below. - ## Note that the 404 is the intended behavior. - log_not_found off; - } - - # Do the same for multilingual private files. - location ^~ /ar/system/files/ { - include apps/drupal/fastcgi_drupal.conf; - fastcgi_pass phpcgi; - log_not_found off; - } - location ^~ /en/system/files/ { - include apps/drupal/fastcgi_drupal.conf; - fastcgi_pass phpcgi; - log_not_found off; - } - location ^~ /fr/system/files/ { - include apps/drupal/fastcgi_drupal.conf; - fastcgi_pass phpcgi; - log_not_found off; - } - location ^~ /ru/system/files/ { - include apps/drupal/fastcgi_drupal.conf; - fastcgi_pass phpcgi; - log_not_found off; - } - location ^~ /es/system/files/ { - include apps/drupal/fastcgi_drupal.conf; - fastcgi_pass phpcgi; - log_not_found off; - } - - ## Trying to access private files directly returns a 404. - location ^~ /sites/default/private/ { - internal; - } - - ## Trying to access private files directly returns a 404. - location ^~ /sites/(.*)/private/ { - internal; - } - - ## Support for the file_force module - ## http://drupal.org/project/file_force. - location ^~ /(ar|en|fr|ru|es)/system/files_force/ { - ## Include the specific FastCGI configuration. This is for a - ## FCGI backend like php-cgi or php-fpm. - include apps/drupal/fastcgi_drupal.conf; - fastcgi_pass phpcgi; - - ## If proxying to apache comment the two lines above and - ## uncomment the two lines below. - #proxy_pass http://phpapache/index.php?q=$uri; - #proxy_set_header Connection ''; - - ## For not signaling a 404 in the error log whenever the - ## system/files directory is accessed add the line below. - ## Note that the 404 is the intended behavior. - log_not_found off; - } - - ## If accessing an image generated by Drupal 6 imagecache, serve it - ## directly if available, if not relay the request to Drupal to (re)generate - ## the image. - location ~* /imagecache/ { - ## Image hotlinking protection. If you want hotlinking - ## protection for your images uncomment the following line. - #include apps/drupal/hotlinking_protection.conf; - - access_log off; - expires 30d; - try_files $uri @drupal; - } - - ## Drupal 7 generated image handling, i.e., imagecache in core. See: - ## http://drupal.org/node/371374. - location ~* /files/styles/ { - ## Image hotlinking protection. If you want hotlinking - ## protection for your images uncomment the following line. - #include apps/drupal/hotlinking_protection.conf; - - access_log off; - expires 30d; - try_files $uri @drupal; - } - - ## Advanced Aggregation module CSS - ## support. http://drupal.org/project/advagg. - location ~* /files/advagg_(?:css|js)/ { - gzip_static on; - access_log off; - expires max; - add_header ETag ""; - add_header Cache-Control "max-age=31449600, no-transform, public"; - add_header Accept-Ranges ''; - try_files $uri $uri/ @drupal; - } - - ## Location for public derivative images to avoid hitting Drupal for invalid - ## image derivative paths or if the source image doesn't exist. - location ^~ /sites/default/files/styles/ { - - ## Valid public derivative image paths. - location ~ "^/sites/default/files/styles/[^/]+/public/(?.*)(\.webp)?$" { - access_log off; - expires 30d; - ## No need to bleed constant updates. Send the all shebang in one - ## fell swoop. - tcp_nodelay off; - ## Set the OS file cache. - open_file_cache max=3000 inactive=120s; - open_file_cache_valid 45s; - open_file_cache_min_uses 2; - open_file_cache_errors off; - - ## Return the derivative image if it already exists or ask Drupal - ## to generate it otherwise. - try_files $uri @drupal-generate-derivative-image; - } - - ## Simply return a 404 for unrecognized derivative image paths. - return 404; - } - - ## All static files will be served directly. - location ~* ^.+\.(?:css|cur|js|jpe?g|gif|htc|ico|png|html|otf|ttf|eot|webp|woff|svg)$ { - - access_log off; - expires 30d; - ## No need to bleed constant updates. Send the all shebang in one - ## fell swoop. - tcp_nodelay off; - ## Set the OS file cache. - open_file_cache max=3000 inactive=120s; - open_file_cache_valid 45s; - open_file_cache_min_uses 2; - open_file_cache_errors off; - } - - ## PDFs and powerpoint files handling. - location ~* ^.+\.(?:pdf|pptx?)$ { - expires 30d; - ## No need to bleed constant updates. Send the all shebang in one - ## fell swoop. - tcp_nodelay off; - } - - ## MP3 and Ogg/Vorbis files are served using AIO when supported. Your OS must support it. - location ~* /files/audio/mp3/.*\.mp3$ { - directio 4k; # for XFS - ## If you're using ext3 or similar uncomment the line below and comment the above. - #directio 512; # for ext3 or similar (block alignments) - tcp_nopush off; - aio on; - output_buffers 1 2M; - } - - location ~* /files/audio/ogg/.*\.ogg$ { - directio 4k; # for XFS - ## If you're using ext3 or similar uncomment the line below and comment the above. - #directio 512; # for ext3 or similar (block alignments) - tcp_nopush off; - aio on; - output_buffers 1 2M; - } - - ## Pseudo streaming of FLV files: - ## http://wiki.nginx.org/HttpFlvStreamModule. - ## If pseudo streaming isn't working, try to comment - ## out in nginx.conf line with: - ## add_header X-Frame-Options SAMEORIGIN; - # location ^~ /sites/default/files/video/flv { - # location ~* ^/sites/default/files/video/flv/.*\.flv$ { - # flv; - # } - # } - - ## Pseudo streaming of H264/AAC files. This requires an Nginx - ## version greater or equal to 1.0.7 for the stable branch and - ## greater or equal to 1.1.3 for the development branch. - ## Cf. http://nginx.org/en/docs/http/ngx_http_mp4_module.html. - # location ^~ /sites/default/files/video/mp4 { # videos - # location ~* ^/sites/default/files/video/mp4/.*\.(?:mp4|mov)$ { - # mp4; - # mp4_buffer_size 1M; - # mp4_max_buffer_size 5M; - # } - # } - - # location ^~ /sites/default/files/audio/m4a { # audios - # location ~* ^/sites/default/files/audio/m4a/.*\.m4a$ { - # mp4; - # mp4_buffer_size 1M; - # mp4_max_buffer_size 5M; - # } - # } - - ## Replicate the Apache directive of Drupal standard - ## .htaccess. Disable access to any code files. Return a 404 to curtail - ## information disclosure. Hide also the text files. - ## And amend the list with Drupal 8/9/10 yaml files. - location ~* ^(?:.+\.(?:htaccess|make|txt|engine|inc|info|install|module|profile|po|pot|sh|.*sql|test|theme|twig|tpl(?:\.php)?|xtmpl|yaml|yml)(~|\.sw[op]|\.bak|\.orig|\.save)?|code-style\.pl|/Entries.*|/Repository|/Root|/Tag|/Template|)$ { - return 404; - } - - ## Extend the list with Drupal 8/9/10 composer files and the rest of the new shipped .htaccess list :-) - location ~* (composer\.(json|lock)|web\.config)|^#.*#$|\.php(~|\.sw[op]|\.bak|\.orig|\.save)$ { - return 404; - } - - ## First we try the URI and relay to the /index.php?q=$uri&$args if not found. - try_files $uri @drupal; -} - -########### Security measures ########## - -## Uncomment the line below if you want to enable basic auth for -## access to all /admin URIs. Note that this provides much better -## protection if use HTTPS. Since it can easily be eavesdropped if you -## use HTTP. -#include apps/drupal/admin_basic_auth.conf; - -## Restrict access to the strictly necessary PHP files. Reducing the -## scope for exploits. Handling of PHP code and the Drupal event loop. -location @drupal { - ## Include the FastCGI config. - include apps/drupal/fastcgi_drupal.conf; - fastcgi_pass phpcgi; - - ## If proxying to apache comment the two lines above and - ## uncomment the two lines below. - #proxy_pass http://phpapache/index.php?q=$uri; - #proxy_set_header Connection ''; - - ## Filefield Upload progress - ## http://drupal.org/project/filefield_nginx_progress support - ## through the NginxUploadProgress modules. - # track_uploads uploads 60s; -} - -location @drupal-no-args { - ## Include the specific FastCGI configuration. This is for a - ## FCGI backend like php-cgi or php-fpm. - include apps/drupal/fastcgi_no_args_drupal.conf; - fastcgi_pass phpcgi; - - ## If proxying to apache comment the two lines above and - ## uncomment the two lines below. - #proxy_pass http://phpapache/index.php?q=$uri; - #proxy_set_header Connection ''; -} - -## Internal location to ask Drupal to generate a derivative image if the source -## image is present. -## Comment out the source image test for HumanitarianAction, so that webp module works. -## See https://humanitarian.atlassian.net/browse/OPS-8805 -location @drupal-generate-derivative-image { - ## If the source image doesn't exist return a 404. - ## if (!-f "$document_root/sites/default/files/$file_path") { - ## return 404; - ## } - - ## Otherwise pass the request to drupal. - try_files /dev/null @drupal; -} - -## Disallow access to .bzr, .git, .hg, .svn, .cvs directories: return -## 404 as not to disclose information. -location ^~ /.bzr { - return 404; -} - -location ^~ /.git { - return 404; -} - -location ^~ /.hg { - return 404; -} - -location ^~ /.svn { - return 404; -} - -location ^~ /.cvs { - return 404; -} - -## Disallow access to patches directory. -location ^~ /patches { - return 404; -} - -## Disallow access to drush backup directory. -location ^~ /backup { - return 404; -} - -## Disable access logs for robots.txt. -location = /robots.txt { - access_log off; - ## Add support for the robotstxt module - ## http://drupal.org/project/robotstxt. - try_files $uri @drupal-no-args; -} - -## RSS feed support. -location = /rss.xml { - try_files $uri @drupal-no-args; -} - -## XML Sitemap support. -location = /sitemap.xml { - try_files $uri @drupal-no-args; -} - -## Support for favicon. Return an 1x1 transparent GIF if it doesn't -## exist. -location = /favicon.ico { - expires 30d; - try_files /favicon.ico @empty; -} - -## Return an in memory 1x1 transparent GIF. -location @empty { - expires 30d; - empty_gif; -} - -## Any other attempt to access PHP files returns a 404. -location ~* ^.+\.php$ { - return 404; -} - -if ( $args ~* "/autocomplete/" ) { - return 406; - ### error_page 406 = @drupal; ### error_page directive is not allowed within pseudo-location -} - -error_page 406 = @drupal; diff --git a/docker/etc/nginx/apps/drupal/fastcgi_drupal.conf b/docker/etc/nginx/apps/drupal/fastcgi_drupal.conf deleted file mode 100644 index db38bb36a..000000000 --- a/docker/etc/nginx/apps/drupal/fastcgi_drupal.conf +++ /dev/null @@ -1,44 +0,0 @@ -#-*- mode: nginx; mode: flyspell-prog; ispell-local-dictionary: "american" -*- -### fastcgi configuration for serving private files. -## 1. Parameters. -fastcgi_param QUERY_STRING $args; -fastcgi_param REQUEST_METHOD $request_method; -fastcgi_param CONTENT_TYPE $content_type; -fastcgi_param CONTENT_LENGTH $content_length; - -fastcgi_param SCRIPT_NAME /index.php; -fastcgi_param REQUEST_URI $request_uri; -fastcgi_param DOCUMENT_URI $document_uri; -fastcgi_param DOCUMENT_ROOT $document_root; -fastcgi_param SERVER_PROTOCOL $server_protocol; - -fastcgi_param GATEWAY_INTERFACE CGI/1.1; -fastcgi_param SERVER_SOFTWARE nginx/$nginx_version; - -fastcgi_param REMOTE_ADDR $remote_addr; -fastcgi_param REMOTE_PORT $remote_port; -fastcgi_param SERVER_ADDR $server_addr; -fastcgi_param SERVER_PORT $server_port; -fastcgi_param SERVER_NAME $server_name; -## PHP only, required if PHP was built with --enable-force-cgi-redirect -fastcgi_param REDIRECT_STATUS 200; -fastcgi_param SCRIPT_FILENAME $document_root/index.php; -## HTTPS 'on' parameter. This requires Nginx version 1.1.11 or -## later. The if_not_empty flag was introduced in 1.1.11. See: -## http://nginx.org/en/CHANGES. If using a version that doesn't -## support this comment out the line below. -fastcgi_param HTTPS $fastcgi_https if_not_empty; -## For Nginx versions below 1.1.11 uncomment the line below after commenting out the above. -#fastcgi_param HTTPS $fastcgi_https; - -## 2. Nginx FCGI specific directives. -fastcgi_buffers 256 4k; -fastcgi_intercept_errors on; -## Allow 4 hrs - pass timeout responsibility to upstream. -fastcgi_read_timeout 14400; -fastcgi_index index.php; -## Hide the Drupal cache headers. -fastcgi_hide_header 'X-Drupal-Cache'; -fastcgi_hide_header 'X-Drupal-Dynamic-Cache'; -## Hide the Drupal header X-Generator. -fastcgi_hide_header 'X-Generator'; diff --git a/docker/etc/nginx/custom/03_mapbox.conf b/docker/etc/nginx/custom/03_mapbox.conf index 088acea61..f3cbde8ee 100644 --- a/docker/etc/nginx/custom/03_mapbox.conf +++ b/docker/etc/nginx/custom/03_mapbox.conf @@ -21,14 +21,24 @@ location /mapbox/ { ## Remove cookies. more_clear_input_headers Cookie; + ## Ensure the proxy host is in a variable, so nginx will not cache the IP + ## indefinitely and cause 502 errors if the IPs change. + set $mapbox_host "api.mapbox.com"; + ## Proxy the request to the mapbox API. proxy_ssl_server_name on; - proxy_set_header Host "api.mapbox.com"; - proxy_pass https://api.mapbox.com$mapbox_request_uri; + proxy_set_header Host $mapbox_host; + proxy_pass https://$mapbox_host$mapbox_request_uri; proxy_http_version 1.1; proxy_redirect off; proxy_intercept_errors on; + ## Substitute the token in the response body. + ## That doesn't seem to have any ill effect. + sub_filter_types application/json; + sub_filter_once off; + sub_filter "$mapbox_token" 'token'; + ## Remove the sku parameter as it's always different. set $cache_uri $mapbox_request_uri; if ($cache_uri ~ "^(.*)[?&]sku=[^&]+(.*)$") { diff --git a/docker/settings.php b/docker/settings.php index 026a81da5..e98e77844 100644 --- a/docker/settings.php +++ b/docker/settings.php @@ -31,12 +31,21 @@ 'collation' => getenv('DRUPAL_DB_COLLATION'), ]); -// Load everything else from snippets under /srv/www/shared/settings. -// @TODO: Use some sort of key/value store. +// Inject some settings for local use and/or Drupal sanity checks. +$settings['config_sync_directory'] = dirname($app_root) . '/config'; +$settings['hash_salt'] = 'sodium-chloride'; + +/** + * Load generated settings. + * + * Load everything else from snippets under /srv/www/shared/settings, where Ansible + * puts them. Do *not* add any (local) settings overrides below this block, as they + * will then override the Ansible-managed ones! + * + * @TODO: Use some sort of key/value store or vault. + */ if (file_exists('/srv/www/shared/settings')) { foreach (glob('/srv/www/shared/settings/settings.*.php') as $filename) { - include_once $filename; + include $filename; } } - -$settings['config_sync_directory'] = dirname($app_root) . '/config'; diff --git a/html/modules/custom/ghi_content/tests/src/Kernel/ImportManagerTest.php b/html/modules/custom/ghi_content/tests/src/Kernel/ImportManagerTest.php index 3f97c3c84..426ef27e6 100644 --- a/html/modules/custom/ghi_content/tests/src/Kernel/ImportManagerTest.php +++ b/html/modules/custom/ghi_content/tests/src/Kernel/ImportManagerTest.php @@ -62,8 +62,8 @@ protected function setUp(): void { $this->installEntitySchema('file'); $this->installConfig(['system', 'field', 'file']); - $this->nodeType = NodeType::create(['type' => self::BUNDLE])->save(); - $this->vocabulary = $this->createVocabulary(); + NodeType::create(['type' => self::BUNDLE])->save(); + $this->createVocabulary(); } /** diff --git a/html/modules/custom/ghi_gin/css/gin_toolbar.css b/html/modules/custom/ghi_gin/css/gin_toolbar.css index b789ce0f5..8e12eee91 100644 --- a/html/modules/custom/ghi_gin/css/gin_toolbar.css +++ b/html/modules/custom/ghi_gin/css/gin_toolbar.css @@ -1,3 +1,13 @@ +.gin--horizontal-toolbar .region-sticky { + display: block; +} +.gin--horizontal-toolbar .region-sticky .region-sticky__items { + width: auto; +} +.gin--horizontal-toolbar nav#toolbar-bar { + width: auto; +} + /* Fix annoying issues with disappearing elements on iOS. */ .gin--horizontal-toolbar #toolbar-administration .toolbar-lining, .ui-dialog {