diff --git a/README.rst b/README.rst index b7d10c2..7cbd4ed 100644 --- a/README.rst +++ b/README.rst @@ -16,14 +16,13 @@ and on top of that: default eases management (especially with tools such as the turnkey script wrappers) but also has potential security implications. - **Security note**: Updates to Drupal may require supervision so + **Security note**: Updates to Drupal require supervision so they **ARE NOT** configured to install automatically. See below for - updating Drupal. And/or see `Drupal 8 documentation`_ + updating Drupal. See the `Drupal update documentation`_ for details. - - Includes drush_, drupal-console_ and composer_ for command line - administration, configuration and development. Also includes relevant - wrapper scripts; turnkey-drush_, turnkey-drupal_ & turnkey-composer_ (run - the relevant command, but as www-data user). + - Includes drush_ and composer_ for command line administration, + configuration and development. The turnkey-drush_ and turnkey-composer_ + wrappers run these tools as the www-data user. - Drupal security update alerts delivered to your inbox - requires `Security Alerts`_ ('secalerts') be enabled on firstboot with a valid @@ -35,7 +34,7 @@ and on top of that: swap - a temporary swap file is acceptable). - Bundled Drupal 10 modules and dependencies (installed to - /var/www/drupal10/web/modules - new default for Drupal10): + /var/www/drupal10/web/modules/contrib): - `Field group`_: Allows fields to be grouped together. - `Google analytics`_: Adds Google Analytics js tracking code to all @@ -69,9 +68,7 @@ details, please consult the `Drupal Upgrade docs`_, we recommend using `Composer to update from the commandline`_ (via turnkey-composer_):: cd /var/www/drupal10 - # update composer - not strictly necessary - composer self-update - turnkey-composer update drupal/core --with-dependencies + turnkey-composer update "drupal/core-*" --with-all-dependencies turnkey-drush updatedb -y turnkey-drush cr @@ -97,12 +94,10 @@ Credentials *(passwords set at first boot)* .. _TurnKey Core: https://www.turnkeylinux.org/core .. _composer: https://getcomposer.org/ .. _drush: https://www.drush.org/ -.. _drupal-console: https://drupalconsole.com/ -.. _turnkey-drush: https://github.com/turnkeylinux-apps/drupal8/blob/master/overlay/usr/local/bin/turnkey-drush -.. _turnkey-drupal: https://github.com/turnkeylinux-apps/drupal8/blob/master/overlay/usr/local/bin/turnkey-drupal +.. _turnkey-drush: https://github.com/turnkeylinux-apps/drupal10/blob/master/overlay/usr/local/bin/turnkey-drush .. _turnkey-composer: https://github.com/turnkeylinux/common/blob/master/overlays/composer/usr/local/bin/turnkey-composer .. _Security Alerts: https://www.turnkeylinux.org/docs/automatic-security-alerts -.. _Drupal 8 documentation: https://www.drupal.org/docs/8/update +.. _Drupal update documentation: https://www.drupal.org/docs/updating-drupal .. _Field group: https://www.drupal.org/project/field_group .. _Google analytics: https://www.drupal.org/project/google_analytics .. _Honeypot: https://www.drupal.org/project/honeypot diff --git a/changelog b/changelog index f9f3949..e0c9b8c 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,20 @@ +turnkey-drupal10-19.0 (1) turnkey; urgency=low + + * Upgrade the base distribution to Debian 13/Trixie and PHP 8.4. + + * Update Drupal to 10.6.15 with Drush 13.7.6 and compatible pinned versions + of the bundled contributed modules. + + * Use Drupal core-recommended's Guzzle 7.15.3 dependency, which includes the + upstream fixes for CVE-2026-69245 and CVE-2026-69246. + + * Create the welcome page through Drupal's entity API and update the + security advisory check for current Drush. + + * See the TurnKey Core 19.0 changelog for changes common to all appliances. + + -- TurnKey Linux release engineering Tue, 25 Aug 2026 00:00:00 +0000 + turnkey-drupal10-18.1 (1) turnkey; urgency=low * v18.1 rebuild - includes latest Debian & TurnKey packages. diff --git a/conf.d/main b/conf.d/main index 8df9d4f..b157047 100755 --- a/conf.d/main +++ b/conf.d/main @@ -7,11 +7,10 @@ DB_PASS=$(mcookie) ADMIN_NAME=admin ADMIN_PASS=turnkey -SRC=/usr/local/src WEBROOT=/var/www/drupal10 -PHP_V=8.2 -USER=www-data +PHP_V=8.4 +WEB_USER=www-data # set proxy settings [ "$FAB_HTTP_PROXY" ] && export HTTP_PROXY=$FAB_HTTP_PROXY @@ -27,43 +26,33 @@ export COMPOSER_MEMORY_LIMIT=-1 # temporarily disable composer memory limits dur # install drupal10 mkdir -p $WEBROOT -chown $USER:$USER $WEBROOT -turnkey-composer create-project drupal/recommended-project:10.x $WEBROOT --no-dev --no-interaction +chown $WEB_USER:$WEB_USER $WEBROOT +turnkey-composer create-project drupal/recommended-project:10.6.15 $WEBROOT --no-dev --no-interaction cd $WEBROOT -# install drush -cd $WEBROOT -turnkey-composer require drush/drush --no-interaction - CONF=$WEBROOT/web/sites/default/settings.php cp $WEBROOT/web/sites/default/default.settings.php $CONF LIBRARIES_PATH=$WEBROOT/libraries mkdir -p $LIBRARIES_PATH/ -chown -R $USER:$USER $WEBROOT +chown -R $WEB_USER:$WEB_USER $WEBROOT service mysql start -mysqladmin create $DB_NAME -mysql --batch --execute "grant all privileges on $DB_NAME.* to $DB_USER@localhost identified by '$DB_PASS'; flush privileges;" +mysql --batch --execute "CREATE DATABASE $DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;" +mysql --batch --execute "CREATE USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';" +mysql --batch --execute "GRANT ALL PRIVILEGES ON $DB_NAME.* TO '$DB_USER'@'localhost';" mysql --batch --execute "set global transaction isolation level read committed;" -# download and install modules -MODULES="drupal/ctools \ - drupal/field_group \ - drupal/google_analytics \ - drupal/imce \ - drupal/pathauto \ - drupal/token" - -# drupal/honeypot -# -# currently doesn't work on drupal10, if you want this re-included upstream -# would appreciate help, read the thread -# https://www.drupal.org/project/honeypot/issues/2943526 - -for module in $MODULES; do - turnkey-composer require $module --no-interaction -done +# Install the maintained command-line tools and bundled modules at the versions +# validated for the v19 appliance. +turnkey-composer require --no-interaction --update-no-dev \ + drush/drush:13.7.6 \ + drupal/ctools:4.1.1 \ + drupal/field_group:4.0.0 \ + drupal/google_analytics:4.0.3 \ + drupal/imce:3.1.5 \ + drupal/pathauto:1.15.0 \ + drupal/token:1.17.0 turnkey-drush site-install standard -y \ --account-name=$ADMIN_NAME \ @@ -83,8 +72,8 @@ turnkey-drush cache-rebuild # configure apache a2dissite 000-default a2ensite drupal10 -a2enmod rewrite - +a2enmod rewrite + # insert "www.example.com" as default trusted_host (inithook will adjust) echo "\$settings['trusted_host_patterns'] = ['^www\.example\.com\$'];" >> $CONF @@ -120,17 +109,11 @@ DRUSH="/usr/local/bin/turnkey-drush" EOF chmod +x $CRON_DRUPAL -# create welcome page -MYSQL_BATCH="mysql --batch --execute" - +# Create the welcome page through Drupal so entity revisions and field storage +# remain consistent with the installed schema. WELCOME='

Please follow these steps to set up and start using your website:

  1. Configure your website: To get started, log in as admin and visit the administration section, where you can customize and configure all aspects of your website.
  2. Start posting content: When ready, delete this welcome and create content for your website.

For more information, please refer to the TurnKey Drupal10 release notes, help section, or the online Drupal handbooks. You may also post at the Drupal forum, or view the wide range of other support options available.

' - -$MYSQL_BATCH "INSERT INTO $DB_NAME.node (nid, vid, type, uuid, langcode) VALUES (1,1,'page','dbab9403-2ebd-4fb9-8a00-e764ce980ecd','en');" -$MYSQL_BATCH "INSERT INTO $DB_NAME.node__body (bundle, deleted, entity_id, revision_id, langcode, delta, body_value, body_summary, body_format) VALUES ('page',0,1,1,'en',0,'$WELCOME','$WELCOME','basic_html');" -$MYSQL_BATCH "INSERT INTO $DB_NAME.node_field_data (nid, vid, type, langcode, title, uid, status, created, changed, promote, sticky, revision_translation_affected, default_langcode) VALUES (1,1,'page','en','Welcome to TurnKey Drupal',1,1,1435831165,1435831165,1,0,1,1);" -$MYSQL_BATCH "INSERT INTO $DB_NAME.node_field_revision (nid, vid, langcode, title, uid, status, created, changed, promote, sticky, revision_translation_affected, default_langcode) VALUES (1,1,'en','Welcome to TurnKey Drupal',1,1,1435831165,1435831165,1,0,1,1);" -$MYSQL_BATCH "INSERT INTO $DB_NAME.node_revision (nid, vid, langcode, revision_timestamp, revision_uid, revision_log) VALUES (1,1,'en',1435831535,1,'');" -$MYSQL_BATCH "INSERT INTO $DB_NAME.node_revision__body (bundle, deleted, entity_id, revision_id, langcode, delta, body_value, body_summary, body_format) VALUES ('page',0,1,1,'en',0,'$WELCOME','$WELCOME','basic_html');" +WELCOME_B64=$(printf '%s' "$WELCOME" | base64 -w0) +turnkey-drush php:eval "\$node = \\Drupal\\node\\Entity\\Node::create(['type' => 'page', 'title' => 'Welcome to TurnKey Drupal', 'body' => ['value' => base64_decode('$WELCOME_B64'), 'format' => 'basic_html'], 'status' => 1, 'promote' => 1]); \$node->save();" service mysql stop diff --git a/docs/v19.0-testing.md b/docs/v19.0-testing.md new file mode 100644 index 0000000..0a1c3fc --- /dev/null +++ b/docs/v19.0-testing.md @@ -0,0 +1,95 @@ +# Drupal 10 19.0 testing + +## Scope and source decision + +Debian 13 Trixie does not package the Drupal application. Drupal 10.6.15 and +the contributed modules therefore come from Drupal's official Composer +repository. Drush and transitive PHP dependencies come from Packagist. The +build requests exact application and module versions, and Composer records the +resolved distributions in `composer.lock`. +Drupal 10.6.15's `core-recommended` lock selects Guzzle 7.15.3, which includes +the fixes for CVE-2026-69245 and CVE-2026-69246 without overriding Drupal's +tested dependency set. + +PHP 8.4, Apache, MariaDB, Postfix and Composer come from signed Debian Trixie +repositories. Drupal 10.6 supports PHP 8.4. The supported update path is a +supervised Composer update followed by Drupal database updates and a cache +rebuild, as documented in the README. + +## Acceptance command + +```sh +/sandboxed-git/turnkey/tools/test-v19-appliance drupal10 \ + --source /home/agent/.local/worktrees/turnkey-apps/drupal10/wish-drupal10-v19-trixie +``` + +This command builds the Trixie root filesystem, imports it into Docker, boots +the normal systemd and firstboot path, then runs `tests/v19.sh`. + +## README crosswalk + +| README contract | Focused check | Required result | Evidence | +| --- | --- | --- | --- | +| Drupal is served over TLS | Request the landing page through Apache | The welcome page is returned over HTTPS | `tests/v19.sh` | +| Firstboot configures the administrator | Log in through Drupal's real web form using the preseeded password | Drupal creates an authenticated administrator session | `tests/v19.sh` | +| Drupal supports normal content management | Create a Basic page through the authenticated web form, read it from MariaDB, restart Apache and MariaDB, then request it again | The page is visible through Drupal and persists across service restarts | `tests/v19.sh` | +| Drush and Composer support command-line administration | Query versions, run cron, validate the Composer lock and inspect the current security command | Drupal 10.6.15 and Drush 13.7.6 run successfully; Composer and cron pass | `tests/v19.sh` | +| The bundled contributed modules are installed | Query enabled modules and the IMCE installation path | CTools, Field Group, Google Analytics, Pathauto and Token are enabled; IMCE is installed | `tests/v19.sh` | +| Drupal security alerts use current tooling | Run the scheduled helper as its configured cron identity, verify the wrappers' effective application user and audit the lock file | Root starts the helper, the wrappers drop to the web user and Composer finds no known advisory | `tests/v19.sh` | +| Webmin, Adminer and local application mail are available | Request both management endpoints and inspect the loopback SMTP listener | Webmin and Adminer answer over HTTPS; Postfix listens locally | `tests/v19.sh` | +| Debian and Drupal components have maintained update paths | Refresh APT metadata and perform a non-mutating Composer core update resolution | Trixie candidates remain eligible; the Composer update resolves without changing the deployment | `tests/v19.sh` | +| Root SSH, backup and other base services are inherited from Core | Cite the unchanged Core layer | Core 19 baseline passes | Core run `20260824t010251z-1634-32241`, source `24c82ee3540ce545422742b0e28ba6b687c53ec2`, verdict `PASS` | + +## Cheap compatibility preflight + +A disposable `debian:trixie-slim` container installed Drupal 10.6.15 and the +exact selected module set on PHP 8.4.24. Composer's platform, lock validation +and security audit checks passed. The resolved Drupal dependency set retained +Guzzle 7.15.3. The full appliance run is responsible for the installed-site +and firstboot behavior. + +The preflight ran these commands after installing Debian's Composer and PHP +packages: + +```sh +composer create-project --no-dev --no-interaction \ + drupal/recommended-project:10.6.15 /tmp/drupal +composer require --no-interaction --update-no-dev \ + drush/drush:13.7.6 drupal/ctools:4.1.1 \ + drupal/field_group:4.0.0 drupal/google_analytics:4.0.3 \ + drupal/imce:3.1.5 drupal/pathauto:1.15.0 drupal/token:1.17.0 +composer check-platform-reqs +composer validate --no-check-publish --no-interaction +composer install --dry-run --no-dev --no-interaction +composer audit --locked --no-interaction +composer update "drupal/core-*" --with-all-dependencies \ + --dry-run --no-interaction +``` + +## Evidence status + +The Drupal 10.6.15 candidate at source commit +`3c1fb0daf8267eb730bc59703457af57d45d140e` passed exact Docker run +`20260829t164241z-6730-30442`. It established the complete README crosswalk: +build and import, normal systemd boot, firstboot, administrator login, content +creation with MariaDB readback, service restart persistence, cron, bundled +modules, management endpoints, the scheduled Composer security audit through +its root cron entry and web-user wrapper, and both supported updater channels. +This candidate used three of six product fix loops. The report SHA-256 is +`bc286beada670bb799e2624dfb8efd9e2e2d7ef5cb698516b117de19e048bb72`, the +run log SHA-256 is +`35758cc8d0e74a1e721a59cc5c353df8d5c59294e41fe7543e3369edf3d0299e`, and +the `SHA256SUMS` manifest SHA-256 is +`b9de86de60a8f59002e8bfffab92f57e7157b59c4e386007401f8c4deac2581e`. +The harness reported `verdict=PASS` and verified cleanup. + +## Deferred issues + +The focused test confirms the local Postfix listener and Drupal mail +configuration but does not deliver mail through an external relay. External +mail delivery depends on site-specific relay credentials and is not required +to establish the appliance's main flow. + +The Docker runtime does not repeat installer, kernel or bootloader checks. +Drupal adds no appliance-specific behavior at those boundaries, so the proven +Core 19 baseline supplies that inherited evidence. diff --git a/overlay/etc/cron.d/drush-mail-securityupdates b/overlay/etc/cron.d/drush-mail-securityupdates index 8b7c7bc..acb244e 100644 --- a/overlay/etc/cron.d/drush-mail-securityupdates +++ b/overlay/etc/cron.d/drush-mail-securityupdates @@ -1 +1 @@ -0 0 * * * www-data [ -x /usr/local/sbin/drush-mail-securityupdates ] && /usr/local/sbin/drush-mail-securityupdates +0 0 * * * root [ -x /usr/local/sbin/drush-mail-securityupdates ] && /usr/local/sbin/drush-mail-securityupdates diff --git a/overlay/usr/lib/inithooks/bin/drupal10.py b/overlay/usr/lib/inithooks/bin/drupal10.py index 78cc037..dfb5a6b 100755 --- a/overlay/usr/lib/inithooks/bin/drupal10.py +++ b/overlay/usr/lib/inithooks/bin/drupal10.py @@ -86,10 +86,18 @@ def main(): m = MySQL() m.execute('UPDATE drupal10.users_field_data SET mail=%s WHERE name=\"admin\";', (email,)) m.execute('UPDATE drupal10.users_field_data SET init=%s WHERE name=\"admin\";', (email,)) - subprocess.run(['turnkey-drush', '-y', 'config-set', 'contact.form.feedback', 'recipients', email]) - subprocess.run(['turnkey-drush', '-y', 'config-set', 'update.settings', 'notification.emails.0', email]) - subprocess.run(['turnkey-drush', '-y', 'config-set', 'system.site', 'mail', email]) - subprocess.run(['turnkey-drush', 'user-password', 'admin', password]) + subprocess.run([ + 'turnkey-drush', '-y', 'config-set', 'contact.form.feedback', + 'recipients', email + ], check=True) + subprocess.run([ + 'turnkey-drush', '-y', 'config-set', 'update.settings', + 'notification.emails.0', email + ], check=True) + subprocess.run([ + 'turnkey-drush', '-y', 'config-set', 'system.site', 'mail', email + ], check=True) + subprocess.run(['turnkey-drush', 'user-password', 'admin', password], check=True) conf = '/var/www/drupal10/web/sites/default/settings.php' conf_tmp = f'{conf}.tmp' shutil.move(conf, conf_tmp) @@ -113,7 +121,7 @@ def main(): os.chown(conf, uid, gid) os.chmod(conf, 0o444) print('Data updated; clearing caches') - subprocess.run(['/etc/cron.hourly/drupal10']) + subprocess.run(['/etc/cron.hourly/drupal10'], check=True) if __name__ == "__main__": main() diff --git a/overlay/usr/lib/inithooks/firstboot.d/40drupal10 b/overlay/usr/lib/inithooks/firstboot.d/40drupal10 index 0ca9fb9..de2047a 100755 --- a/overlay/usr/lib/inithooks/firstboot.d/40drupal10 +++ b/overlay/usr/lib/inithooks/firstboot.d/40drupal10 @@ -1,5 +1,5 @@ #!/bin/bash -e -# set drupal9 admin password and email +# set Drupal 10 admin password, email and domain . /etc/default/inithooks diff --git a/overlay/usr/local/sbin/drush-mail-securityupdates b/overlay/usr/local/sbin/drush-mail-securityupdates index f2c7345..be3a237 100755 --- a/overlay/usr/local/sbin/drush-mail-securityupdates +++ b/overlay/usr/local/sbin/drush-mail-securityupdates @@ -1,10 +1,24 @@ -#!/bin/bash -e +#!/bin/bash +set -e cd /var/www/drupal10 TMPFILE=$(mktemp) -trap "rm -f $TMPFILE" INT TERM EXIT -/usr/local/bin/drush pm-updatestatus --security-only > $TMPFILE +trap 'rm -f "$TMPFILE"' INT TERM EXIT -if grep -q 'SECURITY UPDATE' $TMPFILE; then - cat $TMPFILE | mail -s 'Drupal security updates available' root -fi +set +e +/usr/local/bin/turnkey-composer audit --locked --no-interaction \ + --abandoned=report >"$TMPFILE" 2>&1 +status=$? +set -e + +case $status in + 0) + ;; + 1) + mail -s 'Drupal security updates available' root <"$TMPFILE" + ;; + *) + cat "$TMPFILE" >&2 + exit "$status" + ;; +esac diff --git a/tests/v19.sh b/tests/v19.sh new file mode 100755 index 0000000..5631d46 --- /dev/null +++ b/tests/v19.sh @@ -0,0 +1,158 @@ +#!/bin/bash +set -Eeuo pipefail +umask 077 + +result=${TKL_TEST_RESULT:?TKL_TEST_RESULT is required} +app_password=${TKL_TEST_APP_PASS:?TKL_TEST_APP_PASS is required} +base=https://localhost +cookie=/tmp/tkl-drupal10-cookie.$$ +page=/tmp/tkl-drupal10-page.$$ +headers=/tmp/tkl-drupal10-headers.$$ +policy=/tmp/tkl-drupal10-policy.$$ + +report_error() { + printf 'test_failure line=%s status=%s command=%q\n' \ + "$1" "$2" "$3" >&2 + exit "$2" +} +trap 'report_error "$LINENO" "$?" "$BASH_COMMAND"' ERR +trap 'rm -f -- "$cookie" "$page" "$headers" "$policy"' EXIT + +input_value() { + local name=$1 + local file=$2 + sed -n "s/.*name=\"$name\"[^>]*value=\"\([^\"]*\)\".*/\1/p" \ + "$file" | head -n 1 +} + +systemctl --quiet is-active apache2.service mariadb.service postfix.service \ + multi-user.target +systemctl --quiet is-enabled apache2.service mariadb.service postfix.service +apache2ctl -t +php_version=$(php -r 'echo PHP_MAJOR_VERSION, ".", PHP_MINOR_VERSION, ".", PHP_RELEASE_VERSION;') +[[ $php_version == 8.4.* ]] + +cd /var/www/drupal10 +drupal_version=$(turnkey-drush status --field=drupal-version) +drush_version=$(turnkey-composer show drush/drush --format=json | \ + php -r '$j=json_decode(stream_get_contents(STDIN), true); echo preg_replace("/^[*v ]+/", "", $j["versions"][0]);') +test "$drupal_version" = 10.6.15 +test "$drush_version" = 13.7.6 +turnkey-composer validate --no-check-publish --no-interaction >/dev/null +turnkey-composer install --dry-run --no-dev --no-interaction >/dev/null +turnkey-composer audit --locked --no-interaction >/dev/null + +for package_version in \ + 'drupal/ctools 4.1.1' \ + 'drupal/field_group 4.0.0' \ + 'drupal/google_analytics 4.0.3' \ + 'drupal/imce 3.1.5' \ + 'drupal/pathauto 1.15.0' \ + 'drupal/token 1.17.0' \ + 'guzzlehttp/guzzle 7.15.3'; do + read -r package expected <<<"$package_version" + actual=$(turnkey-composer show "$package" --format=json | \ + php -r '$j=json_decode(stream_get_contents(STDIN), true); echo preg_replace("/^[*v ]+/", "", $j["versions"][0]);') + test "$actual" = "$expected" +done + +enabled_modules=$(turnkey-drush pm:list --type=module --status=enabled --format=list) +for module in ctools field_group google_analytics pathauto token; do + grep -Fxq "$module" <<<"$enabled_modules" +done +test -d web/modules/contrib/imce +turnkey-drush cron >/dev/null +security_cron_user=$(awk '/drush-mail-securityupdates/ {print $6}' \ + /etc/cron.d/drush-mail-securityupdates) +test "$security_cron_user" = root +runuser -u "$security_cron_user" -- \ + /usr/local/sbin/drush-mail-securityupdates +test "$(turnkey-drush php:eval 'echo posix_geteuid();')" = \ + "$(id -u www-data)" + +curl --insecure --fail --silent --show-error --location \ + "$base/" >"$page" +grep -Fq 'Welcome to TurnKey Drupal' "$page" + +curl --insecure --fail --silent --show-error \ + -c "$cookie" "$base/user/login" >"$page" +form_build_id=$(input_value form_build_id "$page") +test -n "$form_build_id" +curl --insecure --fail --silent --show-error --location \ + -b "$cookie" -c "$cookie" "$base/user/login" \ + --data-urlencode 'name=admin' \ + --data-urlencode "pass=$app_password" \ + --data-urlencode "form_build_id=$form_build_id" \ + --data-urlencode 'form_id=user_login_form' \ + --data-urlencode 'op=Log in' >"$page" +grep -Eq 'Log out|user/logout' "$page" +grep -q 'SSESS' "$cookie" + +curl --insecure --fail --silent --show-error \ + -b "$cookie" "$base/node/add/page" >"$page" +form_build_id=$(input_value form_build_id "$page") +form_token=$(input_value form_token "$page") +test -n "$form_build_id" +test -n "$form_token" +curl --insecure --fail --silent --show-error --location \ + -b "$cookie" -c "$cookie" -D "$headers" \ + "$base/node/add/page" \ + --data-urlencode 'title[0][value]=TurnKey v19 acceptance page' \ + --data-urlencode 'body[0][value]=Created through the Drupal web interface' \ + --data-urlencode 'body[0][format]=basic_html' \ + --data-urlencode 'status[value]=1' \ + --data-urlencode "form_build_id=$form_build_id" \ + --data-urlencode "form_token=$form_token" \ + --data-urlencode 'form_id=node_page_form' \ + --data-urlencode 'op=Save' >"$page" +grep -Fq 'TurnKey v19 acceptance page' "$page" +grep -Fq 'Created through the Drupal web interface' "$page" + +nid=$(mariadb --batch --skip-column-names drupal10 --execute \ + "SELECT nid FROM node_field_data WHERE title='TurnKey v19 acceptance page' ORDER BY nid DESC LIMIT 1") +test -n "$nid" +mariadb --batch --skip-column-names drupal10 --execute \ + "SELECT body_value FROM node__body WHERE entity_id=$nid" | \ + grep -Fxq 'Created through the Drupal web interface' + +systemctl restart mariadb.service apache2.service +curl --insecure --fail --silent --show-error \ + "$base/node/$nid" >"$page" +grep -Fq 'TurnKey v19 acceptance page' "$page" +grep -Fq 'Created through the Drupal web interface' "$page" + +dpkg-query -W webmin-apache webmin-mysql >/dev/null +curl --insecure --fail --silent --show-error --head \ + https://127.0.0.1:12321/ >/dev/null +curl --insecure --fail --silent --show-error --head \ + https://127.0.0.1:12322/ >/dev/null +ss -ltn | grep -Eq '127\.0\.0\.1:25[[:space:]]' + +lock_before=$(sha256sum composer.lock | awk '{print $1}') +turnkey-composer update 'drupal/core-*' --with-all-dependencies \ + --dry-run --no-interaction >/dev/null +lock_after=$(sha256sum composer.lock | awk '{print $1}') +test "$lock_after" = "$lock_before" + +apt-get update >/dev/null +for package in apache2 mariadb-server php8.4; do + apt-cache policy "$package" >"$policy" + candidate=$(awk '/Candidate:/ {print $2}' "$policy") + test -n "$candidate" + test "$candidate" != '(none)' + grep -Eq 'trixie|deb13' "$policy" +done +grep -Rqs '^Suites: trixie' /etc/apt/sources.list.d +! grep -Rqi bookworm /etc/apt/sources.list.d + +apache_version=$(dpkg-query -W -f='${Version}' apache2) +mariadb_version=$(dpkg-query -W -f='${Version}' mariadb-server) +cat >"$result" <