diff --git a/README.rst b/README.rst index 696e252..645e45b 100644 --- a/README.rst +++ b/README.rst @@ -11,13 +11,16 @@ support. This appliance includes all the standard features in `TurnKey Core`_, and on top of that: -- XOOPS configurations: +- XOOPS 2.7 configuration: - Installed from upstream source code to /var/www/xoops + - Pinned to the official stable release commit and archive digest. + - ``turnkey-xoops-update`` checks the official XOOPS 2.7 stable channel + and prepares supervised upgrades without replacing site data. **Security note**: Updates to XOOPS may require supervision so they **ARE NOT** configured to install automatically. See "Upgrading - from previous versions" section of the `Xoops release notes`_. + from previous versions" section of the `XOOPS release notes`_. - SSL support out of the box. - `Adminer`_ administration frontend for MySQL (listening on port @@ -36,5 +39,5 @@ Credentials *(passwords set at first boot)* .. _XOOPS: https://xoops.org/ .. _TurnKey Core: https://www.turnkeylinux.org/core -.. _XOOPS release notes: https://github.com/XOOPS/XoopsCore25/blob/master/release_notes.txt +.. _XOOPS release notes: https://github.com/XOOPS/XoopsCore27/blob/master/release_notes.txt .. _Adminer: https://www.adminer.org diff --git a/changelog b/changelog index 44fa0ac..67af415 100644 --- a/changelog +++ b/changelog @@ -1,3 +1,18 @@ +turnkey-xoops-19.0 (1) turnkey; urgency=low + + * Rebase the appliance on Debian 13/Trixie. + + * Install XOOPS 2.7.3 from its pinned official release commit with archive + integrity verification. + + * Preserve the existing firstboot administrator and database credential + contract while storing the administrator password with PHP's current + password hash. + + * Add a supervised updater for the official XOOPS 2.7 stable channel. + + -- TurnKey Linux Developers Tue, 25 Aug 2026 00:00:00 +0000 + turnkey-xoops-18.0 (1) turnkey; urgency=low * Upgraded to latest upstream version of XOOPS - 2.5.11. @@ -270,4 +285,3 @@ turnkey-xoops-12.0 (1) turnkey; urgency=low appliances. Here we only describe changes specific to this appliance. -- Alon Swartz Wed, 01 Aug 2012 08:00:00 +0200 - diff --git a/conf.d/downloads b/conf.d/downloads index b9fabc7..1f06352 100755 --- a/conf.d/downloads +++ b/conf.d/downloads @@ -1,13 +1,16 @@ -#!/bin/bash -ex +#!/bin/bash -e -dl() { - [ "$FAB_HTTP_PROXY" ] && PROXY="--proxy $FAB_HTTP_PROXY" - cd $2; curl -L -f -O $PROXY $1; cd - -} +set -x +VERSION=2.7.3 +COMMIT=00fff34c11fd4bee606e734d3bccf52d36fe0fda +SHA256=b49d935116352434a60e5dae93efce356d9e5f360ad92bc72dcc69d7ab82b8cb +URL="https://github.com/XOOPS/XoopsCore27/archive/$COMMIT.tar.gz" +ARCHIVE="/usr/local/src/$COMMIT.tar.gz" -VERSION="v2.5.11" -URL="https://github.com/XOOPS/XoopsCore25/archive/$VERSION.tar.gz" - -dl $URL /usr/local/src - +curl_args=(-fsSL) +if [ -n "${FAB_HTTP_PROXY:-}" ]; then + curl_args+=(--proxy "$FAB_HTTP_PROXY") +fi +curl "${curl_args[@]}" "$URL" -o "$ARCHIVE" +echo "$SHA256 $ARCHIVE" | sha256sum --check --status diff --git a/conf.d/main b/conf.d/main index 7faa68b..5aded27 100755 --- a/conf.d/main +++ b/conf.d/main @@ -1,118 +1,153 @@ -#!/bin/sh -ex +#!/bin/bash -e + +set -x DB_NAME=xoops DB_USER=xoops DB_PREFIX=xoops -DB_PASS=$(mcookie) ADMIN_NAME=admin -ADMIN_PASS=turnkey ADMIN_MAIL=admin@example.com SRC=/usr/local/src WEBROOT=/var/www/xoops XOOPS=/var/local/lib/xoops -VERSION=v2.5.11 +VERSION=2.7.3 +COMMIT=00fff34c11fd4bee606e734d3bccf52d36fe0fda +ARCHIVE_SHA256=b49d935116352434a60e5dae93efce356d9e5f360ad92bc72dcc69d7ab82b8cb +SOURCE_DIR="$SRC/XoopsCore27-$COMMIT" -# unpack and configure -tar -zxf $SRC/$VERSION.tar.gz -C $SRC -cp -a $SRC/XoopsCore*/htdocs $WEBROOT -cp $SRC/XoopsCore*/extras/mainfile.php $WEBROOT -rm -rf $SRC/XoopsCore* +# Unpack and configure the official XOOPS release. +tar -zxf "$SRC/$COMMIT.tar.gz" -C "$SRC" +install -d "$WEBROOT" +cp -a "$SOURCE_DIR/htdocs/." "$WEBROOT/" -mkdir -p $XOOPS -mv $WEBROOT/xoops_lib $XOOPS/lib -mv $WEBROOT/xoops_data $XOOPS/data +install -d "$XOOPS" +mv "$WEBROOT/xoops_lib" "$XOOPS/lib" +mv "$WEBROOT/xoops_data" "$XOOPS/data" +rm -rf "$SOURCE_DIR" -chown -R www-data:www-data $WEBROOT $XOOPS +chown -R www-data:www-data "$WEBROOT" "$XOOPS" -# configure apache +# Configure Apache and start the services required by the web installer. a2dissite 000-default a2ensite xoops a2enmod rewrite - -# start services service mysql start service apache2 start -# setup the database -MYSQL_BATCH="mysql --user=root --password=$MYSQL_PASS --batch" -MYSQL_ADMIN="mysqladmin --user=root --password=$MYSQL_PASS" - -$MYSQL_ADMIN create $DB_NAME -$MYSQL_BATCH --execute "grant all privileges on $DB_NAME.* to $DB_USER@localhost identified by '$DB_PASS'; flush privileges;" - -# curl based install -XMAIL=$(echo $ADMIN_MAIL | sed s/@/%40/) -XROOT=$(echo $WEBROOT | sed "s/\//%2F/g") -XDATA=$(echo $XOOPS/data | sed "s/\//%2F/g") -XLIB=$(echo $XOOPS/lib | sed "s/\//%2F/g") - -URL="http://127.0.0.1/install" -CURL="curl -c /tmp/cookie -b /tmp/cookie" - -$CURL ${URL}/index.php -H 'Cookie: xo_install_lang=english' --data 'lang=english' -$CURL ${URL}/page_start.php -$CURL ${URL}/page_modcheck.php -$CURL ${URL}/page_pathsettings.php --data "root=$XROOT&data=$XDATA&lib=$XLIB&URL=http%3A%2F%2F127.0.0.1&COOKIE_DOMAIN" -$CURL ${URL}/page_dbconnection.php --data "DB_TYPE=mysql&DB_HOST=localhost&DB_USER=$DB_USER&DB_PASS=$DB_PASS&DB_PCONNECT=1" -$CURL ${URL}/page_dbsettings.php --data "DB_NAME=$DB_NAME&DB_PREFIX=$DB_PREFIX&DB_CHARSET=utf8&DB_COLLATION=utf8_general_ci" -$CURL ${URL}/page_configsave.php +# Keep generated credentials and installer requests out of the build trace. +# Errexit remains active throughout this section. +set +x +DB_PASS=$(mcookie) +ADMIN_PASS=turnkey +MYSQL_BATCH=(mysql --user=root "--password=${MYSQL_PASS:-}" --batch) +MYSQL_ADMIN=(mysqladmin --user=root "--password=${MYSQL_PASS:-}") + +"${MYSQL_ADMIN[@]}" create "$DB_NAME" +"${MYSQL_BATCH[@]}" < /usr/local/share/turnkey-xoops/source < Options +FollowSymLinks AllowOverride All - order allow,deny - allow from all + Require all granted - diff --git a/overlay/usr/lib/inithooks/bin/xoops.py b/overlay/usr/lib/inithooks/bin/xoops.py index ff9bc60..b15aa8c 100755 --- a/overlay/usr/lib/inithooks/bin/xoops.py +++ b/overlay/usr/lib/inithooks/bin/xoops.py @@ -2,7 +2,7 @@ """Set Xoops admin password and email Option: - --pass= unless provided, will ask interactively + --pass-stdin read the password from standard input --email= unless provided, will ask interactively """ @@ -10,7 +10,6 @@ import sys import getopt import subprocess -import hashlib from libinithooks import inithooks_cache from libinithooks.dialog_wrapper import Dialog @@ -26,21 +25,31 @@ def usage(s=None): def main(): try: opts, args = getopt.gnu_getopt(sys.argv[1:], "h", - ['help', 'pass=', 'email=']) + ['help', 'pass=', 'pass-stdin', 'email=']) except getopt.GetoptError as e: usage(e) password = "" + password_stdin = False email = "" for opt, val in opts: if opt in ('-h', '--help'): usage() elif opt == '--pass': password = val + elif opt == '--pass-stdin': + password_stdin = True elif opt == '--email': email = val - if not password: + if password and password_stdin: + usage('--pass and --pass-stdin are mutually exclusive') + + if password_stdin: + password = sys.stdin.read() + if not password: + usage('standard input did not contain a password') + elif not password: d = Dialog('TurnKey Linux - First boot configuration') password = d.get_password( "Xoops Password", @@ -57,14 +66,26 @@ def main(): inithooks_cache.write('APP_EMAIL', email) - hash = hashlib.md5(password.encode('utf8')).hexdigest() + password_hash = subprocess.run( + [ + '/usr/bin/php', + '-r', + 'echo password_hash(stream_get_contents(STDIN), PASSWORD_DEFAULT);', + ], + input=password, + text=True, + capture_output=True, + check=True, + ).stdout + + if not password_hash.startswith('$'): + raise RuntimeError('PHP did not return a modern password hash') m = MySQL() - m.execute('UPDATE xoops.xoops_users SET pass=%s WHERE uname=\"admin\";', (hash,)) + m.execute('UPDATE xoops.xoops_users SET pass=%s WHERE uname=\"admin\";', (password_hash,)) m.execute('UPDATE xoops.xoops_users SET email=%s WHERE uname=\"admin\";', (email,)) m.execute('UPDATE xoops.xoops_config SET conf_value=%s WHERE conf_name=\"adminmail\";', (email,)) if __name__ == "__main__": main() - diff --git a/overlay/usr/lib/inithooks/firstboot.d/20regen-xoops-secrets b/overlay/usr/lib/inithooks/firstboot.d/20regen-xoops-secrets index a834cc2..420375c 100755 --- a/overlay/usr/lib/inithooks/firstboot.d/20regen-xoops-secrets +++ b/overlay/usr/lib/inithooks/firstboot.d/20regen-xoops-secrets @@ -1,11 +1,14 @@ #!/bin/bash -e # regenerate xoops mysql password -. /etc/default/inithooks - CONF=/var/local/lib/xoops/data/data/secure.php PASSWORD=$(mcookie) -sed -i "s|XOOPS_DB_PASS.*|XOOPS_DB_PASS', '$PASSWORD');|" $CONF -$INITHOOKS_PATH/bin/mysqlconf.py --user=xoops --pass="$PASSWORD" - +sed -i "s|XOOPS_DB_PASS.*|XOOPS_DB_PASS', '$PASSWORD');|" "$CONF" +mysql --batch <&2 + exit 2 +} + +source_value() { + local key=$1 + sed -n "s/^${key}=//p" "$SOURCE_FILE" | head -n 1 +} + +[[ $# -ge 1 && $# -le 2 ]] || usage +mode=$1 +dry_run=false +case "$mode" in + --check) + [[ $# -eq 1 ]] || usage + ;; + --apply) + if [[ $# -eq 2 ]]; then + [[ $2 = --dry-run ]] || usage + dry_run=true + fi + ;; + *) + usage + ;; +esac + +[[ -r $SOURCE_FILE && -r $APP_ROOT/include/version.php ]] || { + echo "XOOPS installation provenance is unavailable" >&2 + exit 1 +} + +installed=$(source_value version) +installed_commit=$(source_value commit) +runtime=$(php -r 'define("XOOPS_ROOT_PATH", dirname($argv[1], 2)); require $argv[1]; echo XOOPS_VERSION;' \ + "$APP_ROOT/include/version.php") +[[ $installed =~ ^2\.7\.[0-9]+$ ]] || { + echo "Installed XOOPS source version is invalid" >&2 + exit 1 +} +[[ $installed_commit =~ ^[0-9a-f]{40}$ ]] || { + echo "Installed XOOPS source commit is invalid" >&2 + exit 1 +} +[[ $runtime == "XOOPS $installed"* ]] || { + echo "XOOPS runtime version differs from its source record" >&2 + exit 1 +} + +release_json=$(curl -fsSL "$RELEASE_API") +tag=$(php -r ' + $release = json_decode(stream_get_contents(STDIN), true, 512, JSON_THROW_ON_ERROR); + if (($release["draft"] ?? true) || ($release["prerelease"] ?? true)) { + exit(1); + } + $tag = $release["tag_name"] ?? ""; + if (!preg_match("/^v2\\.7\\.[0-9]+$/", $tag)) { + exit(1); + } + echo $tag; +' <<<"$release_json") +latest=${tag#v} + +candidate=$(git ls-remote "$UPSTREAM_REPO" "refs/tags/$tag^{}" \ + | awk 'NR == 1 { print $1 }') +if [[ -z $candidate ]]; then + candidate=$(git ls-remote "$UPSTREAM_REPO" "refs/tags/$tag" \ + | awk 'NR == 1 { print $1 }') +fi +[[ $candidate =~ ^[0-9a-f]{40}$ ]] || { + echo "The official XOOPS release tag could not be resolved" >&2 + exit 1 +} + +status=update-available +if [[ $installed = "$latest" && $installed_commit = "$candidate" ]]; then + status=up-to-date +fi + +if [[ $mode = --check ]]; then + printf 'installed=%s\ninstalled_commit=%s\nruntime=%s\n' \ + "$installed" "$installed_commit" "$runtime" + printf 'latest=%s\ncandidate=%s\ntag=%s\n' "$latest" "$candidate" "$tag" + printf 'channel=official-xoops-core27-stable\nstatus=%s\n' "$status" + exit 0 +fi + +if $dry_run; then + printf 'mode=apply-dry-run\ntarget=%s\ncandidate=%s\ntag=%s\n' \ + "$latest" "$candidate" "$tag" + printf 'verified=official-xoops-release-tag\nstatus=%s\n' "$status" + printf 'upgrade_path=/upgrade/\n' + exit 0 +fi + +[[ $EUID -eq 0 ]] || { + echo "--apply must run as root" >&2 + exit 1 +} + +if [[ $status = up-to-date ]]; then + echo "XOOPS $installed is already current" + exit 0 +fi + +stage=$(mktemp -d /var/tmp/turnkey-xoops-update.XXXXXX) +cleanup() { + rm -rf "$stage" +} +trap cleanup EXIT + +archive="$stage/$candidate.tar.gz" +curl -fsSL "https://github.com/XOOPS/XoopsCore27/archive/$candidate.tar.gz" \ + -o "$archive" +archive_sha256=$(sha256sum "$archive" | awk '{ print $1 }') +tar -xzf "$archive" -C "$stage" +release_root="$stage/XoopsCore27-$candidate" +[[ -d $release_root/htdocs && -d $release_root/upgrade ]] || { + echo "The official XOOPS archive has an unexpected layout" >&2 + exit 1 +} +grep -Fq "XOOPS $latest" "$release_root/htdocs/include/version.php" || { + echo "The official XOOPS archive version differs from its release tag" >&2 + exit 1 +} + +backup_dir=/var/backups/turnkey-xoops +backup_id=$(date -u +%Y%m%dT%H%M%SZ) +install -d -m 0700 "$backup_dir" +mysqldump --single-transaction "$DB_NAME" \ + | gzip -c > "$backup_dir/xoops-$backup_id.sql.gz" +tar -C / -czf "$backup_dir/xoops-$backup_id-files.tar.gz" \ + var/www/xoops var/local/lib/xoops + +tar -C "$release_root/htdocs" \ + --exclude=./mainfile.php \ + --exclude=./uploads \ + --exclude=./xoops_lib \ + --exclude=./xoops_data \ + -cf - . | tar -C "$APP_ROOT" -xf - +tar -C "$release_root/htdocs/xoops_lib" -cf - . \ + | tar -C "$DATA_ROOT/lib" -xf - +tar -C "$release_root/htdocs/xoops_data" \ + --exclude=./caches \ + --exclude=./data/secure.php \ + --exclude=./logs \ + --exclude=./protector \ + -cf - . | tar -C "$DATA_ROOT/data" -xf - +cp -a "$release_root/upgrade" "$APP_ROOT/upgrade" + +chown -R root:root "$APP_ROOT" "$DATA_ROOT" +chown -R www-data:www-data "$APP_ROOT/uploads" +chown -R www-data:www-data "$DATA_ROOT/data/caches" +chown -R www-data:www-data "$DATA_ROOT/data/data" +chown -R www-data:www-data "$DATA_ROOT/data/logs" +chown -R www-data:www-data "$DATA_ROOT/data/protector" +chown -R www-data:www-data "$DATA_ROOT/lib/modules/protector/configs" +chown root:www-data "$APP_ROOT/mainfile.php" "$DATA_ROOT/data/data/secure.php" +chmod 0440 "$APP_ROOT/mainfile.php" "$DATA_ROOT/data/data/secure.php" + +updated_runtime=$(php -r 'define("XOOPS_ROOT_PATH", dirname($argv[1], 2)); require $argv[1]; echo XOOPS_VERSION;' \ + "$APP_ROOT/include/version.php") +cat > "$SOURCE_FILE" < #include +git php-gd php-cli php-curl php-intl php-mbstring php-xml - -unzip - diff --git a/tests/v19.sh b/tests/v19.sh new file mode 100755 index 0000000..b288ec9 --- /dev/null +++ b/tests/v19.sh @@ -0,0 +1,162 @@ +#!/bin/bash +set -euo pipefail + +: "${TKL_TEST_RESULT:?TKL_TEST_RESULT must name the result file}" +: "${TKL_TEST_APP_PASS:?TKL_TEST_APP_PASS must contain the firstboot XOOPS password}" + +APP_ROOT=/var/www/xoops +SOURCE_FILE=/usr/local/share/turnkey-xoops/source +BASE_URL=https://127.0.0.1 +DB_NAME=xoops + +fail() { + echo "FAIL: $*" >&2 + exit 1 +} + +require_contains() { + local text=$1 + local expected=$2 + local context=$3 + [[ $text == *"$expected"* ]] || fail "$context did not contain: $expected" +} + +source_value() { + local key=$1 + sed -n "s/^${key}=//p" "$SOURCE_FILE" | head -n 1 +} + +[[ -r $SOURCE_FILE ]] || fail "XOOPS source provenance is missing" +[[ -x /usr/local/bin/turnkey-xoops-update ]] || fail "XOOPS updater is not executable" + +version=$(source_value version) +commit=$(source_value commit) +archive_sha256=$(source_value archive_sha256) +channel=$(source_value channel) +recorded_runtime=$(source_value runtime_version) + +[[ $version = 2.7.3 ]] || fail "unexpected XOOPS source version" +[[ $commit = 00fff34c11fd4bee606e734d3bccf52d36fe0fda ]] \ + || fail "unexpected XOOPS source commit" +[[ $archive_sha256 = b49d935116352434a60e5dae93efce356d9e5f360ad92bc72dcc69d7ab82b8cb ]] \ + || fail "unexpected XOOPS archive digest" +[[ $channel = official-xoops-core27-stable ]] || fail "unexpected XOOPS channel" + +runtime=$(php -r 'define("XOOPS_ROOT_PATH", dirname($argv[1], 2)); require $argv[1]; echo XOOPS_VERSION;' \ + "$APP_ROOT/include/version.php") +[[ $runtime = "$recorded_runtime" ]] || fail "runtime version differs from provenance" +[[ $runtime == "XOOPS $version"* ]] || fail "runtime version differs from release tag" + +[[ $(stat -c '%U:%G:%a' "$APP_ROOT/mainfile.php") = root:www-data:440 ]] \ + || fail "XOOPS mainfile permissions are incorrect" +[[ $(stat -c '%U:%G:%a' /var/local/lib/xoops/data/data/secure.php) = root:www-data:440 ]] \ + || fail "XOOPS database credential permissions are incorrect" + +systemctl is-active --quiet apache2 || fail "Apache is not active" +systemctl is-active --quiet mariadb || fail "MariaDB is not active" +apache2ctl configtest 2>&1 | grep -q 'Syntax OK' || fail "Apache configuration is invalid" + +home_page=$(curl --insecure -fsSL --max-time 30 "$BASE_URL/") +require_contains "$home_page" "TurnKey XOOPS" "XOOPS HTTPS home page" +require_contains "$home_page" "Welcome to TurnKey XOOPS" "XOOPS welcome block" + +adminer_page=$(curl --insecure -fsSL --max-time 30 https://127.0.0.1:12322/) +require_contains "$adminer_page" "Adminer" "Adminer HTTPS endpoint" + +cookie=$(mktemp) +pass_file=$(mktemp) +trap 'rm -f "$cookie" "$pass_file"' EXIT +chmod 0600 "$cookie" "$pass_file" +printf '%s' "$TKL_TEST_APP_PASS" > "$pass_file" +curl_args=(--insecure -fsS --max-time 30 -c "$cookie" -b "$cookie") + +curl "${curl_args[@]}" "$BASE_URL/user.php" -o /dev/null +curl "${curl_args[@]}" "$BASE_URL/user.php" \ + --data-urlencode 'uname=admin' \ + --data-urlencode "pass@$pass_file" \ + --data-urlencode 'op=login' \ + -o /dev/null + +admin_page=$(curl "${curl_args[@]}" "$BASE_URL/admin.php") +require_contains "$admin_page" "Control Panel" "XOOPS administrator login" + +block_form=$(curl "${curl_args[@]}" \ + "$BASE_URL/modules/system/admin.php?fct=blocksadmin&op=add") +require_contains "$block_form" "Blocks Administration" "XOOPS block administration" +token=$(sed -n \ + "s/.*name=['\"]XOOPS_TOKEN_REQUEST['\"][^>]*value=['\"]\([^'\"]*\)['\"].*/\1/p" \ + <<<"$block_form" | head -n 1) +[[ -n $token ]] || fail "XOOPS block form did not contain a request token" + +fixture_title='TurnKey XOOPS acceptance block' +fixture_content='XOOPS content create and read verified' +curl "${curl_args[@]}" "$BASE_URL/modules/system/admin.php" \ + --data-urlencode "XOOPS_TOKEN_REQUEST=$token" \ + --data-urlencode 'fct=blocksadmin' \ + --data-urlencode 'op=save' \ + --data-urlencode 'bid=0' \ + --data-urlencode 'block_type=C' \ + --data-urlencode 'mid=0' \ + --data-urlencode 'func_num=0' \ + --data-urlencode 'func_file=' \ + --data-urlencode 'show_func=' \ + --data-urlencode 'edit_func=' \ + --data-urlencode 'template=' \ + --data-urlencode 'dirname=' \ + --data-urlencode 'name=' \ + --data-urlencode 'side=0' \ + --data-urlencode 'weight=0' \ + --data-urlencode 'visible=1' \ + --data-urlencode 'modules[]=-1' \ + --data-urlencode 'groups[]=1' \ + --data-urlencode 'groups[]=2' \ + --data-urlencode 'groups[]=3' \ + --data-urlencode "title=$fixture_title" \ + --data-urlencode "content_block=$fixture_content" \ + --data-urlencode 'c_type=H' \ + --data-urlencode 'bcachetime=0' \ + -o /dev/null + +public_fixture=$(curl --insecure -fsSL --max-time 30 "$BASE_URL/?v19-acceptance=1") +require_contains "$public_fixture" "$fixture_title" "public XOOPS content" +require_contains "$public_fixture" "$fixture_content" "public XOOPS content" + +db_fixture=$(mysql --batch --skip-column-names "$DB_NAME" --execute=" + SELECT CONCAT(title, '|', content) + FROM xoops_newblocks + WHERE title='$fixture_title' + ORDER BY bid DESC LIMIT 1;") +[[ $db_fixture = "$fixture_title|$fixture_content" ]] \ + || fail "XOOPS database did not retain the created content" + +password_scheme=$(mysql --batch --skip-column-names "$DB_NAME" --execute=" + SELECT LEFT(pass, 1) FROM xoops_users WHERE uname='admin';") +[[ $password_scheme = '$' ]] || fail "firstboot did not store a modern admin password hash" + +check_output=$(turnkey-xoops-update --check) +latest=$(awk -F= '$1 == "latest" { print $2 }' <<<"$check_output") +candidate=$(awk -F= '$1 == "candidate" { print $2 }' <<<"$check_output") +tag=$(awk -F= '$1 == "tag" { print $2 }' <<<"$check_output") +[[ $latest =~ ^2\.7\.[0-9]+$ ]] || fail "updater returned an invalid stable version" +[[ $candidate =~ ^[0-9a-f]{40}$ ]] || fail "updater returned an invalid commit" +[[ $tag = "v$latest" ]] || fail "updater tag and version differ" +require_contains "$check_output" "channel=official-xoops-core27-stable" "XOOPS updater" + +apply_plan=$(turnkey-xoops-update --apply --dry-run) +require_contains "$apply_plan" "mode=apply-dry-run" "XOOPS updater plan" +require_contains "$apply_plan" "target=$latest" "XOOPS updater plan" +require_contains "$apply_plan" "candidate=$candidate" "XOOPS updater plan" +require_contains "$apply_plan" "verified=official-xoops-release-tag" "XOOPS updater plan" +require_contains "$apply_plan" "upgrade_path=/upgrade/" "XOOPS updater plan" + +echo "PASS: XOOPS HTTPS admin login, custom block create/read, MariaDB, and updater" +echo "version=$version runtime=$runtime commit=$commit" +cat > "$TKL_TEST_RESULT" <