From 38b7b5456921cb3686cfbfe7f13704443185c003 Mon Sep 17 00:00:00 2001 From: Jannes Date: Fri, 17 Apr 2026 14:10:08 +0200 Subject: [PATCH 1/3] Version 1.1 --- tunnel.sh | 169 ++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 140 insertions(+), 29 deletions(-) diff --git a/tunnel.sh b/tunnel.sh index 1bfd807..1599e9e 100644 --- a/tunnel.sh +++ b/tunnel.sh @@ -1,41 +1,152 @@ -trap ctrl_c INT +#! /bin/bash + +trap ctrl_c INT SIGINT SIGTERM + +## Set Vars +CF_TUNNEL_PUBLIC_URL='' +CF_TUNNEL_HASH='' +PRECHECK_HOME_OPTION='' +WAIT_FOR_CF_URL=12 function ctrl_c() { - echo "Termination caught. Reverting basic-wordpress to defaults:" + echo + echo "Termination caught. Reverting site to basic.wordpress.test." + do_database_actions "$CF_TUNNEL_PUBLIC_URL" "https://basic.wordpress.test" + echo "Site basic-wordpress restored to defaults. Stopping Cloudflare tunnel." + kill_tunnel + echo "Cloudflare tunnel stopped." + exit 0 +} + +function echoerr() { + printf "\033[0;31m%s\n\033[0m" "$*" >&2 +} + +function echoverb() { + printf "\033[0;36m%s\n\033[0m" "$*" >&2 +} + +function box_out() { + local s="$*" + tput setaf 5 + echo "+-${s//?/-}-+ +| ${s//?/ } | +| $(tput setaf 4)$s$(tput setaf 5) | +| ${s//?/ } | ++-${s//?/-}-+" + tput sgr 0 +} + +function do_database_actions() { + local REPLACE_FROM="$1" + local REPLACE_TO="$2" + wp_cli option set home "$REPLACE_TO" + wp_cli option set siteurl "$REPLACE_TO" + wp_cli search-replace "$REPLACE_FROM" "$REPLACE_TO" +} + +function kill_tunnel() { + docker stop "$CF_TUNNEL_HASH" &>$([[ ! $_VERBOSE ]] && echo '/dev/null' || echo '/dev/stdout') +} + +function wp_cli() { + docker exec -ti basic-wordpress wp "$@" $([[ ! $_VERBOSE ]] && echo '--quiet') + return $? +} + +function pre_check_wp_container() { + if docker ps | grep $([[ ! $_VERBOSE ]] && echo '-q') basic-wordpress; then + echo "Container basic-wordpress seems to be running. Proceeding." + else + echoerr "Can't detect basic-wordpress container running. Aborting." + exit 1 + fi +} + +function attempt_restore() { + echo "Attempting restore of old tunnel remnants..." - docker exec -ti basic-wordpress wp option set home https://basic.wordpress.test - docker exec -ti basic-wordpress wp option set siteurl https:/basic.wordpress.test - docker exec -ti basic-wordpress wp search-replace "$CF_TUNNEL_PUBLIC_URL" 'https://basic.wordpress.test' + if [[ $(do_database_actions "$PRECHECK_HOME_OPTION" "https://basic.wordpress.test") -eq 0 ]]; then + echo "Restored to basic settings." + else + echoerr "Restoration failed, verify manually or clean with 'clean.sh'. Aborting." + exit 1 + fi +} - echo "basic-wordpress restored to defaults. Stopping Cloudflare tunnel." - docker stop $CF_TUNNEL_HASH - echo "Done. Enjoy." - exit 0 +function pre_check_wp_database() { + PRECHECK_HOME_OPTION=$(wp_cli option get home | tr -d '[:space:]') + + if [[ "$PRECHECK_HOME_OPTION" != "https://basic.wordpress.test" ]]; then + if [[ "$PRECHECK_HOME_OPTION" =~ .+trycloudflare.com$ ]]; then + echo "Previous 'trycloudflare.com' URL found in the database. This indicates a tunnel was started and not terminated properly. Attempt restore?" + select yn in "Yes" "No"; do + case $yn in + Yes ) attempt_restore; break;; + No ) echoerr "Aborting."; exit 1;; + esac + done + else + echoerr "Default home option (basic.wordpress.test) not found. Running a search-replace may corrupt the database. Please run 'clean.sh' to clean your environment and try again. Aborting." + exit 1 + fi + fi + [[ "$_VERBOSE" ]] && echoverb "Default database option found, we can continue." } -if docker ps | grep basic-wordpress; then - echo "basic-wordpress seems to be running. Proceeding." -else - echo "Can't detect basic-wordpress container running. Aborting." - exit 1 -fi +function start_cf_container() { + if ! CF_TUNNEL_HASH=$(docker run -d cloudflare/cloudflared:latest tunnel --url https://host.docker.internal:443 --no-tls-verify --http-host-header basic.wordpress.test --origin-server-name basic.wordpress.test); then + echoerr "Could not start a proper tunnel container. Aborting." + fi -CF_TUNNEL_HASH=$(docker run -d cloudflare/cloudflared:latest tunnel --url https://host.docker.internal:443 --no-tls-verify --http-host-header basic.wordpress.test --origin-server-name basic.wordpress.test) -echo "Cloudflare docker container hash: $CF_TUNNEL_HASH" + [[ "$_VERBOSE" ]] && echoverb "Tunnel container hash: $CF_TUNNEL_HASH" -echo "Waiting 8 seconds to retrieve your public url." -sleep 8 + echo "Waiting a maximum of $WAIT_FOR_CF_URL seconds for Cloudflare to provide us with your URL." + CF_TUNNEL_PUBLIC_URL="$(timeout "$WAIT_FOR_CF_URL" docker logs -f "$CF_TUNNEL_HASH" 2>&1 | while read -r line; do [[ "$line" =~ INF[\ \|]+https ]] && echo "$line" | grep -E 'INF[ \|]+https' | grep -oE 'https://[^ ]+' && exit 0; done)" -CF_TUNNEL_PUBLIC_URL=$(docker logs $CF_TUNNEL_HASH 2>&1 | grep -E 'INF[ \|]+https' | grep -oE 'https://[^ ]+') + [[ "$CF_TUNNEL_PUBLIC_URL" == '' ]] && echoerr "Could not retrieve a tunnel URL from Cloudflare. Dumping Cloudflare container logs and aborting." && docker logs --tail 20 "$CF_TUNNEL_HASH" && kill_tunnel && exit 1 +} -echo "Performing replacements in the docker basic-wordpress container:" -docker exec -ti basic-wordpress wp option set home $CF_TUNNEL_PUBLIC_URL -docker exec -ti basic-wordpress wp option set siteurl $CF_TUNNEL_PUBLIC_URL -docker exec -ti basic-wordpress wp search-replace 'https://basic.wordpress.test' "$CF_TUNNEL_PUBLIC_URL" +function main() { + pre_check_wp_container + pre_check_wp_database + start_cf_container -echo -echo "Tunnel is set up. You can reach your site at $CF_TUNNEL_PUBLIC_URL" -echo "Please be aware that your are granting public access to a part of your machine. Do not keep this tunnel running for too long." -echo "Press CTRL + C to revert your site to basic.wordpress.test and exit the tunnel." + echo "Performing replacements in the docker basic-wordpress container..." + do_database_actions "https://basic.wordpress.test" "$CF_TUNNEL_PUBLIC_URL" + + echo + echo "Tunnel is set up. Here is your public URL:" + box_out "$CF_TUNNEL_PUBLIC_URL" + echo + echo "Please be aware that your are granting public access to a part of your machine. Do not keep this tunnel running for too long." + echo "Press CTRL + C to revert your site to basic.wordpress.test and exit the tunnel." + + while true; do sleep 60; done +} + +while test $# -gt 0; do + case "$1" in + -v|--verbose) + _VERBOSE=true + shift + ;; + -r|--restore) + _RUN_RESTORE=true + shift + ;; + *) + break + ;; + esac +done + +if [[ "$_RUN_RESTORE" ]]; then + echo "Only running pre-checks and potentially restore." + pre_check_wp_container + pre_check_wp_database + echo "Done." + exit 0 +fi -while true; do sleep 60; done +main \ No newline at end of file From cb07a97f5646e0af371500f6ae88086ca2f0aa3d Mon Sep 17 00:00:00 2001 From: Jannes Date: Fri, 17 Apr 2026 14:22:29 +0200 Subject: [PATCH 2/3] Add docs and comments by Roo Code + Qwen --- tunnel.sh | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 159 insertions(+), 1 deletion(-) diff --git a/tunnel.sh b/tunnel.sh index 1599e9e..7808bcf 100644 --- a/tunnel.sh +++ b/tunnel.sh @@ -1,4 +1,9 @@ #! /bin/bash +# +# Manages Cloudflare tunnel for WordPress development environment. +# Starts a Cloudflare tunnel, updates WordPress database with the tunnel URL, +# and provides cleanup functionality on termination. +# trap ctrl_c INT SIGINT SIGTERM @@ -8,6 +13,17 @@ CF_TUNNEL_HASH='' PRECHECK_HOME_OPTION='' WAIT_FOR_CF_URL=12 +####################################### +# Signal handler for cleanup on termination. +# Reverts WordPress database to default URL and stops the Cloudflare tunnel. +# Globals: +# CF_TUNNEL_PUBLIC_URL - The public URL of the Cloudflare tunnel +# CF_TUNNEL_HASH - The Docker container hash of the tunnel +# Arguments: +# None (signal handler) +# Returns: +# Exits with status 0 after cleanup. +####################################### function ctrl_c() { echo echo "Termination caught. Reverting site to basic.wordpress.test." @@ -18,14 +34,48 @@ function ctrl_c() { exit 0 } +####################################### +# Print error message to stderr in red color. +# Globals: +# None +# Arguments: +# $1 - Error message to print +# Outputs: +# Writes error message to stderr in red color. +# Returns: +# None (uses printf) +####################################### function echoerr() { printf "\033[0;31m%s\n\033[0m" "$*" >&2 } +####################################### +# Print verbose message to stderr in cyan color. +# Globals: +# None +# Arguments: +# $1 - Verbose message to print +# Outputs: +# Writes message to stderr in cyan color. +# Returns: +# None (uses printf) +####################################### function echoverb() { printf "\033[0;36m%s\n\033[0m" "$*" >&2 } +####################################### +# Display a text box with the given string centered. +# Uses terminal colors for visual formatting. +# Globals: +# None +# Arguments: +# $1 - String to display in the box +# Outputs: +# Writes formatted box to stdout. +# Returns: +# None (uses echo and tput) +####################################### function box_out() { local s="$*" tput setaf 5 @@ -37,6 +87,19 @@ function box_out() { tput sgr 0 } +####################################### +# Update WordPress database with new home and siteurl values. +# Performs search-replace of old URL with new URL in the database. +# Globals: +# None +# Arguments: +# $1 - REPLACE_FROM: The old URL to replace +# $2 - REPLACE_TO: The new URL to use +# Outputs: +# Modifies WordPress database options via wp_cli. +# Returns: +# Exit status from wp_cli commands. +####################################### function do_database_actions() { local REPLACE_FROM="$1" local REPLACE_TO="$2" @@ -45,15 +108,52 @@ function do_database_actions() { wp_cli search-replace "$REPLACE_FROM" "$REPLACE_TO" } +####################################### +# Stop the Cloudflare tunnel Docker container. +# Suppresses output unless verbose mode is enabled. +# Globals: +# CF_TUNNEL_HASH - The Docker container hash of the tunnel +# Arguments: +# None +# Outputs: +# None (output suppressed or sent to stdout/stderr) +# Returns: +# Exit status from docker stop command. +####################################### function kill_tunnel() { docker stop "$CF_TUNNEL_HASH" &>$([[ ! $_VERBOSE ]] && echo '/dev/null' || echo '/dev/stdout') } +####################################### +# Execute wp-cli commands inside the basic-wordpress Docker container. +# Adds --quiet flag when verbose mode is disabled. +# Globals: +# _VERBOSE - Controls quiet mode for wp-cli +# Arguments: +# $@ - Arguments passed to wp-cli +# Outputs: +# Command output from wp-cli (unless quiet mode) +# Returns: +# Exit status from wp-cli command. +####################################### function wp_cli() { docker exec -ti basic-wordpress wp "$@" $([[ ! $_VERBOSE ]] && echo '--quiet') return $? } +####################################### +# Verify that the basic-wordpress Docker container is running. +# Exits with error if container is not detected. +# Globals: +# _VERBOSE - Controls verbose output +# Arguments: +# None +# Outputs: +# Success message to stdout if container is running. +# Error message to stderr if container is not running. +# Returns: +# Exits with 1 if container is not running. +####################################### function pre_check_wp_container() { if docker ps | grep $([[ ! $_VERBOSE ]] && echo '-q') basic-wordpress; then echo "Container basic-wordpress seems to be running. Proceeding." @@ -63,6 +163,18 @@ function pre_check_wp_container() { fi } +####################################### +# Restore WordPress database to pre-tunnel state. +# Replaces the previous tunnel URL with the default WordPress URL. +# Globals: +# PRECHECK_HOME_OPTION - The previous home URL from the database +# Arguments: +# None +# Outputs: +# Status messages to stdout/stderr. +# Returns: +# Exits with 1 if restoration fails. +####################################### function attempt_restore() { echo "Attempting restore of old tunnel remnants..." @@ -74,6 +186,19 @@ function attempt_restore() { fi } +####################################### +# Pre-check the WordPress database home option before starting tunnel. +# Detects previous tunnel URLs and prompts for restoration if needed. +# Globals: +# PRECHECK_HOME_OPTION - Set to the current home option value +# _VERBOSE - Controls verbose output +# Arguments: +# None +# Outputs: +# Status messages and prompts to stdout/stderr. +# Returns: +# Exits with 1 if database is not in expected state. +####################################### function pre_check_wp_database() { PRECHECK_HOME_OPTION=$(wp_cli option get home | tr -d '[:space:]') @@ -91,9 +216,25 @@ function pre_check_wp_database() { exit 1 fi fi + [[ "$_VERBOSE" ]] && echoverb "Default database option found, we can continue." } +####################################### +# Start the Cloudflare tunnel Docker container and retrieve the public URL. +# Waits for Cloudflare to provide the tunnel URL from container logs. +# Globals: +# CF_TUNNEL_HASH - Set to the Docker container hash on success +# CF_TUNNEL_PUBLIC_URL - Set to the public tunnel URL on success +# WAIT_FOR_CF_URL - Maximum seconds to wait for URL +# _VERBOSE - Controls verbose output +# Arguments: +# None +# Outputs: +# Status messages to stdout/stderr. +# Returns: +# Exits with 1 if tunnel cannot be started or URL not retrieved. +####################################### function start_cf_container() { if ! CF_TUNNEL_HASH=$(docker run -d cloudflare/cloudflared:latest tunnel --url https://host.docker.internal:443 --no-tls-verify --http-host-header basic.wordpress.test --origin-server-name basic.wordpress.test); then echoerr "Could not start a proper tunnel container. Aborting." @@ -107,6 +248,20 @@ function start_cf_container() { [[ "$CF_TUNNEL_PUBLIC_URL" == '' ]] && echoerr "Could not retrieve a tunnel URL from Cloudflare. Dumping Cloudflare container logs and aborting." && docker logs --tail 20 "$CF_TUNNEL_HASH" && kill_tunnel && exit 1 } +####################################### +# Main entry point for the tunnel management script. +# Orchestrates container verification, database checks, tunnel startup, +# and URL display to the user. +# Globals: +# CF_TUNNEL_PUBLIC_URL - Set by start_cf_container +# CF_TUNNEL_HASH - Set by start_cf_container +# Arguments: +# None +# Outputs: +# Status messages and tunnel URL to stdout/stderr. +# Returns: +# Runs indefinitely until interrupted (via ctrl_c handler). +####################################### function main() { pre_check_wp_container pre_check_wp_database @@ -125,6 +280,8 @@ function main() { while true; do sleep 60; done } +# Parse command-line arguments for optional flags. +# Supports --verbose/-v for verbose output and --restore/-r for pre-check only mode. while test $# -gt 0; do case "$1" in -v|--verbose) @@ -141,6 +298,7 @@ while test $# -gt 0; do esac done +# If --restore flag was provided, only run pre-checks and optionally restore. if [[ "$_RUN_RESTORE" ]]; then echo "Only running pre-checks and potentially restore." pre_check_wp_container @@ -149,4 +307,4 @@ if [[ "$_RUN_RESTORE" ]]; then exit 0 fi -main \ No newline at end of file +main From 7fffefa07c0c3c764c903e41c78b1afc6e3d4fea Mon Sep 17 00:00:00 2001 From: Jannes Date: Fri, 17 Apr 2026 14:40:11 +0200 Subject: [PATCH 3/3] Split long command to its own function for readability --- tunnel.sh | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/tunnel.sh b/tunnel.sh index 7808bcf..95de69f 100644 --- a/tunnel.sh +++ b/tunnel.sh @@ -220,6 +220,26 @@ function pre_check_wp_database() { [[ "$_VERBOSE" ]] && echoverb "Default database option found, we can continue." } +####################################### +# Extract the Cloudflare tunnel public URL from container logs. +# Parses log output to find the HTTPS URL provided by Cloudflare. +# Globals: +# WAIT_FOR_CF_URL - Maximum seconds to wait for URL +# CF_TUNNEL_HASH - The Docker container hash to check logs from +# Arguments: +# None +# Outputs: +# Writes the extracted URL to stdout (if found). +# Returns: +# Exits with 0 when URL is found, otherwise exits with non-zero. +####################################### +function cf_timed_check () { + timeout "$WAIT_FOR_CF_URL" docker logs -f "$CF_TUNNEL_HASH" 2>&1 | \ + while read -r line; do + [[ "$line" =~ INF[\ \|]+https ]] && echo "$line" | grep -E 'INF[ \|]+https' | grep -oE 'https://[^ ]+' && exit 0 + done +} + ####################################### # Start the Cloudflare tunnel Docker container and retrieve the public URL. # Waits for Cloudflare to provide the tunnel URL from container logs. @@ -243,7 +263,7 @@ function start_cf_container() { [[ "$_VERBOSE" ]] && echoverb "Tunnel container hash: $CF_TUNNEL_HASH" echo "Waiting a maximum of $WAIT_FOR_CF_URL seconds for Cloudflare to provide us with your URL." - CF_TUNNEL_PUBLIC_URL="$(timeout "$WAIT_FOR_CF_URL" docker logs -f "$CF_TUNNEL_HASH" 2>&1 | while read -r line; do [[ "$line" =~ INF[\ \|]+https ]] && echo "$line" | grep -E 'INF[ \|]+https' | grep -oE 'https://[^ ]+' && exit 0; done)" + CF_TUNNEL_PUBLIC_URL="$(cf_timed_check)" [[ "$CF_TUNNEL_PUBLIC_URL" == '' ]] && echoerr "Could not retrieve a tunnel URL from Cloudflare. Dumping Cloudflare container logs and aborting." && docker logs --tail 20 "$CF_TUNNEL_HASH" && kill_tunnel && exit 1 } @@ -274,7 +294,7 @@ function main() { echo "Tunnel is set up. Here is your public URL:" box_out "$CF_TUNNEL_PUBLIC_URL" echo - echo "Please be aware that your are granting public access to a part of your machine. Do not keep this tunnel running for too long." + echo "Be aware that your are granting public access to a part of your machine. Do not keep this tunnel running longer than necessary." echo "Press CTRL + C to revert your site to basic.wordpress.test and exit the tunnel." while true; do sleep 60; done