mirror of
https://ghproxy.net/https://github.com/CaptainCore/captaincore.git
synced 2026-07-28 13:07:02 +08:00
235 lines
No EOL
8.6 KiB
Bash
Executable file
235 lines
No EOL
8.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
#
|
|
# Generates quicksave for plugins and themes changes for one or more sites.
|
|
#
|
|
# `captaincore quicksave generate <site>`
|
|
#
|
|
# [<site>...]
|
|
# One or more sites.
|
|
#
|
|
# [@<target>]
|
|
# Target groups of sites like @all @production or @staging.
|
|
#
|
|
# [--parallel=<number>]
|
|
# Number of Quicksaves at same time
|
|
#
|
|
# [--force]
|
|
# Force even if no changes were made.
|
|
#
|
|
# [--debug]
|
|
# Debug mode
|
|
#
|
|
# [--skip-if-recent=<timeframe>]
|
|
# Skip if quicksave generated or last checked within the specified timeframe (e.g., "24h", "7d").
|
|
#
|
|
|
|
if [ ${#@} -ne 1 ]; then
|
|
echo -e "${COLOR_RED}Error:${COLOR_NORMAL} Please specify a <site>."
|
|
exit
|
|
fi
|
|
|
|
while read config; do
|
|
if [[ "$config" == "Error:"* ]]; then
|
|
continue
|
|
fi
|
|
declare "$config"
|
|
done <<< "$(captaincore config fetch --captain-id=$CAPTAIN_ID)"
|
|
|
|
site=$1
|
|
|
|
run_command() {
|
|
|
|
# Extract environment
|
|
if [[ "$site" == *"-staging"* ]]; then
|
|
environment=staging
|
|
else
|
|
environment=production
|
|
fi
|
|
|
|
# Load site configs
|
|
IFS=$'\n'$'\r'; for line in $(captaincore site get $site --bash --captain-id=$CAPTAIN_ID); do declare "$line"; done; unset IFS
|
|
|
|
# Return error if domain not found
|
|
if [[ "$domain" == "" ]] || [[ "$site" == "" ]]; then
|
|
echo "Can't locate website for $site"
|
|
exit
|
|
fi
|
|
|
|
# --------------------------------------------------
|
|
# Locking Mechanism
|
|
# Prevent concurrent quicksaves on the same environment
|
|
# --------------------------------------------------
|
|
|
|
# Ensure the directory exists
|
|
mkdir -p "$path/${site}_${site_id}/${environment}"
|
|
lock_file="$path/${site}_${site_id}/${environment}/quicksave.lock"
|
|
|
|
# Check if lock file exists
|
|
if [ -f "$lock_file" ]; then
|
|
local locked_pid
|
|
locked_pid=$(cat "$lock_file")
|
|
|
|
# Check if the process ID in the lock file is actually running
|
|
if [ -n "$locked_pid" ] && ps -p "$locked_pid" > /dev/null 2>&1; then
|
|
echo "Skipping $site ($environment) - Quicksave currently in process (PID: $locked_pid)"
|
|
exit 0
|
|
else
|
|
# Process is dead, lock is stale. Clean up silently and proceed.
|
|
rm -f "$lock_file"
|
|
fi
|
|
fi
|
|
|
|
# Create the lock file with the current Process ID
|
|
echo $$ > "$lock_file"
|
|
|
|
# Ensure lock file is removed on script exit (Success, Error, or Interrupt)
|
|
cleanup_lock() {
|
|
rm -f "$lock_file"
|
|
}
|
|
trap cleanup_lock EXIT INT TERM
|
|
|
|
# --------------------------------------------------
|
|
# Auto-Resume / Skip Logic
|
|
# Check if we processed this recently based on --skip-if-recent flag
|
|
# --------------------------------------------------
|
|
if [[ -n "$SKIP_IF_RECENT" ]]; then
|
|
quicksave_list_file="$path/${site}_${site_id}/${environment}/quicksaves/list.json"
|
|
should_skip=$(php ${CAPTAINCORE_PATH}/lib/local-scripts/check-last-run.php "$quicksave_list_file" "$SKIP_IF_RECENT")
|
|
|
|
if [[ "$should_skip" == "true" ]]; then
|
|
echo "Skipping $site ($environment) - Checked within last $SKIP_IF_RECENT."
|
|
exit 0
|
|
fi
|
|
fi
|
|
# --------------------------------------------------
|
|
|
|
rclone_config_file="$path/${site}_${site_id}/rclone.conf"
|
|
if [ ! -f "$rclone_config_file" ]; then
|
|
captaincore site key-generate $site --captain-id=$CAPTAIN_ID
|
|
fi
|
|
|
|
# Lookup rclone
|
|
remote_check=$( rclone config show $environment --config="$rclone_config_file" )
|
|
|
|
if [[ $remote_check == *"Couldn't find type of fs"* ]]; then
|
|
echo "$(date +'%Y-%m-%d %H:%M') Generating rclone configs for $site"
|
|
captaincore site key-generate $site --captain-id=$CAPTAIN_ID
|
|
fi
|
|
|
|
# Captures FTP errors in $ftp_output and file listing to log file
|
|
ftp_output=$( { timeout 30 rclone lsd ${environment}:$home_directory --config="$rclone_config_file" --contimeout=15s --timeout=30s ; } 2>&1 )
|
|
ftp_search_for_wordpress=$( echo "$ftp_output" | perl -wnE'say for /wp-admin/g' )
|
|
|
|
# Handle FTP errors
|
|
if [[ $ftp_search_for_wordpress != "wp-admin"* ]]; then
|
|
echo "Can't locate WordPress for ${site}-${environment}"
|
|
exit
|
|
fi
|
|
|
|
# Append trailing slash if home_directory exist
|
|
if [ "$home_directory" != "" ]; then
|
|
home_directory="${home_directory}/"
|
|
fi
|
|
|
|
# --------------------------------------------------
|
|
# Remote Fingerprint Check
|
|
# Generate content fingerprint on remote server and compare against stored fingerprint.
|
|
# If unchanged and not --force, skip rclone sync to save disk I/O.
|
|
# --------------------------------------------------
|
|
site_path="${site}_${site_id}/${environment}"
|
|
fingerprint_file="$path/$site_path/.fingerprint"
|
|
|
|
remote_fingerprint=$(captaincore ssh $site-$environment \
|
|
--script=quicksave-fingerprint \
|
|
--captain-id=$CAPTAIN_ID \
|
|
-- --wp_content=$wp_content 2>/dev/null)
|
|
|
|
if [[ -n "$remote_fingerprint" ]] && [[ -f "$fingerprint_file" ]]; then
|
|
stored_fingerprint=$(cat "$fingerprint_file")
|
|
if [[ "$remote_fingerprint" == "$stored_fingerprint" ]] && [[ "$FLAG_FORCE" != "true" ]]; then
|
|
echo "No file changes detected. Skipping rclone sync."
|
|
captaincore sync-data $site-${environment} --captain-id=$CAPTAIN_ID > /dev/null
|
|
|
|
# Still run quicksave add — core checksum changes (extra/modified root files)
|
|
# won't affect the wp-content fingerprint but should trigger a new quicksave
|
|
captaincore quicksave add $site-$environment --captain-id=$CAPTAIN_ID
|
|
|
|
# Touch list.json to mark site as recently checked
|
|
quicksave_list_file="$path/${site}_${site_id}/${environment}/quicksaves/list.json"
|
|
if [ -f "$quicksave_list_file" ]; then
|
|
touch "$quicksave_list_file"
|
|
fi
|
|
|
|
if [ -f "${path}/process-${process_id}-progress.log" ]; then
|
|
echo -n "." >> ${path}/process-${process_id}-progress.log
|
|
fi
|
|
|
|
exit 0
|
|
fi
|
|
fi
|
|
# --------------------------------------------------
|
|
|
|
quicksave_id_before=$( captaincore quicksave latest $site-$environment --field=hash --captain-id=$CAPTAIN_ID )
|
|
echo "Begin quicksave"
|
|
|
|
# Create directory structure
|
|
mkdir -p $path/$site_path/quicksave/mu-plugins
|
|
mkdir -p $path/$site_path/quicksave/plugins
|
|
mkdir -p $path/$site_path/quicksave/themes
|
|
mkdir -p $path/$site_path/quicksave/versions
|
|
|
|
cd $path/$site_path/quicksave/
|
|
|
|
# Create new git repo if needed
|
|
if [ ! -d ".git" ]; then
|
|
git init
|
|
fi
|
|
|
|
# Sync DIRECTLY to quicksave folders
|
|
# Note: Added --exclude ".git/**" to prevent rclone from messing with the repo metadata if a plugin contains a git repo.
|
|
rclone sync ${environment}:$home_directory${wp_content}/themes/ $path/$site_path/quicksave/themes/ --exclude .DS_Store --exclude *timthumb.txt --exclude "node_modules/**" --exclude ".git/**" --exclude error_log --config="$rclone_config_file"
|
|
rclone sync ${environment}:$home_directory${wp_content}/mu-plugins/ $path/$site_path/quicksave/mu-plugins/ --exclude .DS_Store --exclude *timthumb.txt --exclude "node_modules/**" --exclude ".git/**" --exclude error_log --config="$rclone_config_file"
|
|
rclone sync ${environment}:$home_directory${wp_content}/plugins/ $path/$site_path/quicksave/plugins/ --exclude .DS_Store --exclude *timthumb.txt --exclude "node_modules/**" --exclude ".git/**" --exclude error_log --config="$rclone_config_file"
|
|
|
|
captaincore sync-data $site-${environment} --captain-id=$CAPTAIN_ID > /dev/null
|
|
captaincore quicksave add $site-$environment --captain-id=$CAPTAIN_ID
|
|
|
|
# Save remote fingerprint after successful sync
|
|
if [[ -n "$remote_fingerprint" ]]; then
|
|
echo "$remote_fingerprint" > "$fingerprint_file"
|
|
fi
|
|
|
|
# Remove git lock if found
|
|
if [ -f "$path/$site_path/quicksave/.git/index.lock" ]; then
|
|
rm "$path/$site_path/quicksave/.git/index.lock"
|
|
fi
|
|
|
|
captaincore quicksave list-generate $site-${environment} --captain-id=$CAPTAIN_ID > /dev/null
|
|
quicksave_id_after=$( captaincore quicksave latest $site-$environment --field=hash --captain-id=$CAPTAIN_ID )
|
|
|
|
# Only run vuln-scan and backup if quicksave actually changed
|
|
if [[ "$quicksave_id_before" != "$quicksave_id_after" ]]; then
|
|
captaincore site vuln-scan $site-$environment --captain-id=$CAPTAIN_ID > /dev/null
|
|
captaincore quicksave backup $site-$environment --captain-id=$CAPTAIN_ID
|
|
fi
|
|
|
|
# --------------------------------------------------
|
|
# Touch list.json to update its modification timestamp
|
|
# This marks the time this site was last processed, regardless of content changes.
|
|
# --------------------------------------------------
|
|
quicksave_list_file="$path/${site}_${site_id}/${environment}/quicksaves/list.json"
|
|
if [ -f "$quicksave_list_file" ]; then
|
|
touch "$quicksave_list_file"
|
|
fi
|
|
# --------------------------------------------------
|
|
|
|
if [ -f "${path}/process-${process_id}-progress.log" ]; then
|
|
echo -n "." >> ${path}/process-${process_id}-progress.log
|
|
fi
|
|
|
|
echo " Quicksave complete"
|
|
|
|
}
|
|
|
|
run_command |