do/commands/update
2025-06-21 14:52:40 -04:00

346 lines
13 KiB
Bash
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ----------------------------------------------------
# Update Commands
# Handles WordPress core, theme, and plugin updates.
# ----------------------------------------------------
UPDATE_LOGS_DIR=""
UPDATE_LOGS_LIST_FILE=""
# ----------------------------------------------------
# Ensures update directories and lists exist.
# ----------------------------------------------------
function _ensure_update_setup() {
# Exit if already initialized
if [[ -n "$UPDATE_LOGS_DIR" ]]; then return 0; fi
local private_dir
if ! private_dir=$(_get_private_dir); then
return 1
fi
# Using the checkpoint base for consistency as updates are linked to checkpoints
local checkpoint_base_dir="${private_dir}/checkpoints"
UPDATE_LOGS_DIR="${checkpoint_base_dir}/updates"
UPDATE_LOGS_LIST_FILE="$UPDATE_LOGS_DIR/list.json"
mkdir -p "$UPDATE_LOGS_DIR"
if [ ! -f "$UPDATE_LOGS_LIST_FILE" ]; then
echo "[]" > "$UPDATE_LOGS_LIST_FILE"
fi
}
# ----------------------------------------------------
# (Helper) Lets the user select an update log from the list.
# ----------------------------------------------------
function _select_update_log() {
_ensure_update_setup
if ! setup_wp_cli; then return 1; fi
if [ ! -s "$UPDATE_LOGS_LIST_FILE" ]; then
echo " No update logs found. Run '_do update all' to create one." >&2
return 1
fi
# Use PHP to read the detailed list and check format
local php_script_read_list='
<?php
$list_file = "%s";
$list = json_decode(file_get_contents($list_file), true);
if (!is_array($list) || empty($list)) {
echo "EMPTY";
return;
}
// Check if the first item has the new detailed format.
if (!isset($list[0]["formatted_timestamp"])) {
echo "NEEDS_GENERATE";
return;
}
foreach($list as $item) {
if (isset($item["formatted_timestamp"]) && isset($item["hash_before"]) && isset($item["hash_after"]) && isset($item["counts_str"]) && isset($item["diff_stats"])) {
// Output format: formatted_timestamp|hash_before|hash_after|counts_str|diff_stats
echo $item["formatted_timestamp"] . "|" . $item["hash_before"] . "|" . $item["hash_after"] . "|" . $item["counts_str"] . "|" . $item["diff_stats"] . "\n";
}
}
'
local php_script; php_script=$(printf "$php_script_read_list" "$UPDATE_LOGS_LIST_FILE")
local update_entries; update_entries=$(echo "$php_script" | "$WP_CLI_CMD" eval-file -)
if [[ "$update_entries" == "EMPTY" ]]; then
echo " No update logs available to select." >&2; return 1;
elif [[ "$update_entries" == "NEEDS_GENERATE" ]]; then
echo "⚠️ The update log list needs to be generated for faster display." >&2
echo "Please run: _do update list-generate" >&2
return 1
fi
local display_items=()
local data_items=()
while IFS='|' read -r formatted_timestamp hash_before hash_after counts_str diff_stats; do
hash_before=$(echo "$hash_before" | tr -d '[:space:]')
hash_after=$(echo "$hash_after" | tr -d '[:space:]')
if [ -z "$hash_before" ] || [ -z "$hash_after" ]; then continue; fi
local display_string
display_string=$(printf "%-28s | %s -> %s | %-18s | %s" \
"$formatted_timestamp" "${hash_before:0:7}" "${hash_after:0:7}" "$counts_str" "$diff_stats")
display_items+=("$display_string")
data_items+=("$hash_before|$hash_after")
done <<< "$update_entries"
if [ ${#display_items[@]} -eq 0 ]; then
echo "❌ No valid update entries to display." >&2
return 1
fi
local prompt_text="${1:-Select an update to inspect}"
local selected_display
selected_display=$(printf "%s\n" "${display_items[@]}" | "$GUM_CMD" filter --height=20 --prompt="👇 $prompt_text" --indicator="→" --placeholder="")
if [ -z "$selected_display" ]; then
echo "" # Return empty for cancellation
return 0
fi
local selected_index=-1
for i in "${!display_items[@]}"; do
if [[ "${display_items[$i]}" == "$selected_display" ]]; then
selected_index=$i
break
fi
done
if [ "$selected_index" -ne -1 ]; then
echo "${data_items[$selected_index]}"
return 0
else
echo "❌ Error: Could not find selected update." >&2
return 1
fi
}
# ----------------------------------------------------
# Generates a detailed list of updates for faster access.
# ----------------------------------------------------
function update_list_generate() {
if ! setup_gum || ! setup_git; then return 1; fi
if ! setup_wp_cli; then return 1; fi
_ensure_checkpoint_setup # This sets up repo path as well
_ensure_update_setup
local php_script_read_list='
<?php
$list_file = "%s";
if (!file_exists($list_file)) { return; }
$list = json_decode(file_get_contents($list_file), true);
if (!is_array($list) || empty($list)) { return; }
foreach($list as $item) {
// Read from both simple and potentially detailed formats
$timestamp = $item["timestamp"] ?? "N/A";
$hash_before = $item["before"] ?? $item["hash_before"] ?? null;
$hash_after = $item["after"] ?? $item["hash_after"] ?? null;
if ($timestamp && $hash_before && $hash_after) {
echo "$timestamp|$hash_before|$hash_after\n";
}
}
'
local php_script; php_script=$(printf "$php_script_read_list" "$UPDATE_LOGS_LIST_FILE")
local update_entries; update_entries=$(echo "$php_script" | "$WP_CLI_CMD" eval-file -)
if [ -z "$update_entries" ]; then
echo " No update logs found to generate a list from."
return 0
fi
echo "🔎 Generating detailed update list... (This may take a moment)"
local detailed_items=()
while IFS='|' read -r timestamp hash_before hash_after; do
hash_before=$(echo "$hash_before" | tr -d '[:space:]')
hash_after=$(echo "$hash_after" | tr -d '[:space:]')
if [ -z "$hash_before" ] || [ -z "$hash_after" ]; then continue; fi
# Validate that both commits exist before proceeding
if ! "$GIT_CMD" -C "$CHECKPOINT_REPO_DIR" cat-file -e "${hash_before}^{commit}" &>/dev/null; then
echo "⚠️ Warning: Could not find 'before' commit '$hash_before'. Skipping entry." >&2
continue
fi
if ! "$GIT_CMD" -C "$CHECKPOINT_REPO_DIR" cat-file -e "${hash_after}^{commit}" &>/dev/null; then
echo "⚠️ Warning: Could not find 'after' commit '$hash_after'. Skipping entry." >&2
continue
fi
local manifest_after; manifest_after=$("$GIT_CMD" -C "$CHECKPOINT_REPO_DIR" show "$hash_after:manifest.json" 2>/dev/null)
if [ -z "$manifest_after" ]; then
echo "⚠️ Warning: Could not find manifest for 'after' hash '$hash_after'. Skipping entry." >&2
continue
fi
local php_get_counts='
<?php
$manifest_json = <<<'EOT'
%s
EOT;
$data = json_decode($manifest_json, true);
$theme_count = isset($data["themes"]) && is_array($data["themes"]) ? count($data["themes"]) : 0;
$plugin_count = isset($data["plugins"]) && is_array($data["plugins"]) ? count($data["plugins"]) : 0;
echo "$theme_count Themes, $plugin_count Plugins";
'
local counts_script; counts_script=$(printf "$php_get_counts" "$manifest_after")
local counts_str; counts_str=$(echo "$counts_script" | "$WP_CLI_CMD" eval-file -)
local diff_stats
diff_stats=$("$GIT_CMD" -C "$CHECKPOINT_REPO_DIR" diff --shortstat "$hash_before" "$hash_after" -- 'plugins/' 'themes/' 'mu-plugins/' | sed 's/^[ \t]*//')
if [ -z "$diff_stats" ]; then diff_stats="No file changes"; fi
local formatted_timestamp
if [[ "$(uname)" == "Darwin" ]]; then
formatted_timestamp=$(date -j -f "%Y-%m-%dT%H:%M:%SZ" -u "$timestamp" "+%a, %b %d, %Y, %-I:%M %p")
else
formatted_timestamp=$(date -d "$timestamp" "+%a, %b %d, %Y, %-I:%M %p")
fi
local json_item
json_item=$(printf '{"hash_before": "%s", "hash_after": "%s", "timestamp": "%s", "formatted_timestamp": "%s", "counts_str": "%s", "diff_stats": "%s"}' \
"$hash_before" "$hash_after" "$timestamp" "$formatted_timestamp" "$counts_str" "$diff_stats")
detailed_items+=("$json_item")
done <<< "$update_entries"
local full_json="["
full_json+=$(IFS=,; echo "${detailed_items[*]}")
full_json+="]"
local php_write_script='
<?php
$list_file = "%s";
$json_data = <<<'EOT'
%s
EOT;
$data = json_decode($json_data, true);
file_put_contents($list_file, json_encode($data, JSON_PRETTY_PRINT));
'
local write_script; write_script=$(printf "$php_write_script" "$UPDATE_LOGS_LIST_FILE" "$full_json")
if echo "$write_script" | "$WP_CLI_CMD" eval-file -; then
echo "✅ Detailed update list saved to $UPDATE_LOGS_LIST_FILE"
else
echo "❌ Error: Failed to write detailed update list."
fi
}
# ----------------------------------------------------
# Lists all past updates from the pre-generated list.
# ----------------------------------------------------
function update_list() {
if ! setup_gum || ! setup_git; then return 1; fi
if ! setup_wp_cli; then return 1; fi
_ensure_checkpoint_setup # Ensures repo path is set
local selected_hashes
selected_hashes=$(_select_update_log "Select an update to inspect")
if [ -z "$selected_hashes" ]; then
echo "No update selected."
return 0
fi
if [ $? -ne 0 ]; then
return 1 # Error already printed by helper
fi
local selected_hash_before; selected_hash_before=$(echo "$selected_hashes" | cut -d'|' -f1)
local selected_hash_after; selected_hash_after=$(echo "$selected_hashes" | cut -d'|' -f2)
checkpoint_show "$selected_hash_after" "$selected_hash_before"
}
# ----------------------------------------------------
# Runs the full update process.
# ----------------------------------------------------
function run_update_all() {
if ! setup_git; then return 1; fi
if ! command -v wp &>/dev/null; then echo "❌ Error: WP-CLI not found." >&2; return 1; fi
_ensure_checkpoint_setup
_ensure_update_setup
echo "🚀 Starting full WordPress update process..."
echo " - Step 1/5: Creating 'before' checkpoint..."
checkpoint_create > /dev/null
local hash_before; hash_before=$(checkpoint_latest)
if [ -z "$hash_before" ]; then
echo "❌ Error: Could not create 'before' checkpoint." >&2
return 1
fi
echo " Before Hash: $hash_before"
echo " - Step 2/5: Running WordPress updates..."
echo " - Updating core..."
"$WP_CLI_CMD" core update --skip-plugins --skip-themes
echo " - Updating themes..."
"$WP_CLI_CMD" theme update --all --skip-plugins --skip-themes
echo " - Updating plugins..."
"$WP_CLI_CMD" plugin update --all --skip-plugins --skip-themes
echo " - Step 3/5: Creating 'after' checkpoint..."
checkpoint_create > /dev/null
local hash_after; hash_after=$(checkpoint_latest)
if [ -z "$hash_after" ]; then
echo "❌ Error: Could not create 'after' checkpoint." >&2
return 1
fi
echo " After Hash: $hash_after"
if [ "$hash_before" == "$hash_after" ]; then
echo "✅ No updates were available. Site is up-to-date."
return 0
fi
echo " - Step 4/5: Generating update log entry..."
local timestamp; timestamp=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# This creates a simple entry. `update list-generate` will enrich it later.
local php_list_template='
<?php
$list_file = "%s";
$before = "%s";
$after = "%s";
$timestamp = "%s";
$list = file_exists($list_file) ? json_decode(file_get_contents($list_file), true) : [];
if (!is_array($list)) { $list = []; }
$new_entry = ["before" => $before, "after" => $after, "timestamp" => $timestamp];
// To keep it simple, we just read the simple format and add to it.
// The list-generate command is responsible for creating the detailed format.
$simple_list = [];
foreach($list as $item) {
$simple_list[] = [
"before" => $item["before"] ?? $item["hash_before"] ?? null,
"after" => $item["after"] ?? $item["hash_after"] ?? null,
"timestamp" => $item["timestamp"] ?? null
];
}
array_unshift($simple_list, $new_entry);
echo json_encode($simple_list, JSON_PRETTY_PRINT);
'
local php_list_script; php_list_script=$(printf "$php_list_template" "$UPDATE_LOGS_LIST_FILE" "$hash_before" "$hash_after" "$timestamp")
local temp_list_file; temp_list_file=$(mktemp)
if echo "$php_list_script" | "$WP_CLI_CMD" eval-file - > "$temp_list_file"; then
mv "$temp_list_file" "$UPDATE_LOGS_LIST_FILE"
else
echo "❌ Error: Failed to update master update list." >&2
rm "$temp_list_file"
fi
echo " - Step 5/5: Regenerating detailed update list..."
update_list_generate > /dev/null
echo "✅ Update process complete."
}