mirror of
https://ghproxy.net/https://github.com/CaptainCore/captaincore.git
synced 2026-07-28 13:07:02 +08:00
131 lines
4.3 KiB
Bash
Executable file
131 lines
4.3 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
#
|
|
# Bulk runs a command to many sites.
|
|
#
|
|
# `captaincore bulk <command>`
|
|
#
|
|
# [<site>...]
|
|
# One or more sites.
|
|
#
|
|
# [@<target>]
|
|
# Target groups of sites like @all @production or @staging.
|
|
#
|
|
# [--parallel=<number>]
|
|
# Number of sites to backup at same time
|
|
#
|
|
|
|
# Prevent infinite recursion from re-entering bulk mode
|
|
if [[ "$CC_BULK_RUNNING" == "true" ]]; then
|
|
echo -e "${COLOR_RED}Error:${COLOR_NORMAL} Recursive bulk execution detected. Aborting."
|
|
exit 1
|
|
fi
|
|
export CC_BULK_RUNNING=true
|
|
|
|
while read config; do
|
|
if [[ "$config" == "Error:"* ]]; then
|
|
continue
|
|
fi
|
|
declare "$config"
|
|
done <<< "$(captaincore config fetch --captain-id=$CAPTAIN_ID)"
|
|
|
|
# Error if no sites specifed
|
|
if [ ${#@} -eq 0 ]; then
|
|
echo -e "${COLOR_RED}Error:${COLOR_NORMAL} Please specify <command>."
|
|
exit
|
|
fi
|
|
|
|
cmd=$1
|
|
cmd=${cmd/\// }
|
|
shift # remove command name
|
|
|
|
target=$CAPTAINCORE_ARGS
|
|
count=( $target )
|
|
parallel=$FLAG_PARALLEL
|
|
|
|
# Remove target arguments, leaving only flags
|
|
for ((i = 0; i < ${#count[@]}; i++)); do shift; done
|
|
|
|
# Shell-quote remaining flags for safe transport through env var
|
|
cc_args_quoted=""
|
|
if [[ $# -gt 0 ]]; then
|
|
cc_args_quoted=$(printf '%q ' "$@")
|
|
fi
|
|
|
|
# Error if no sites specifed
|
|
if [[ $cmd == "" ]] || [[ $target == "" ]]; then
|
|
echo -e "${COLOR_RED}Error:${COLOR_NORMAL} Please specify one or more sites, or use a target @all, @production or @staging."
|
|
exit
|
|
fi
|
|
|
|
run_command() {
|
|
|
|
if [[ $target == "@all"* ]] || [[ $target == "@production"* ]] || [[ $target == "@staging"* ]]; then
|
|
target=$( captaincore site list $target --captain-id=$CAPTAIN_ID )
|
|
fi
|
|
|
|
if [[ "$parallel" == "" ]]; then
|
|
parallel=10
|
|
fi
|
|
|
|
site_count=( $target )
|
|
echo "Running '$cmd' on ${#site_count[@]} sites (parallel: $parallel)..."
|
|
|
|
# Set up progress tracking
|
|
progress_dir="$HOME/.captaincore/data/progress"
|
|
mkdir -p "$progress_dir"
|
|
bulk_pid=$$
|
|
progress_meta="$progress_dir/${bulk_pid}.json"
|
|
progress_log="$progress_dir/${bulk_pid}.log"
|
|
|
|
# JSON-escape the args string
|
|
args_escaped=$(printf '%s' "$cc_args_quoted" | sed 's/\\/\\\\/g; s/"/\\"/g')
|
|
target_escaped=$(printf '%s' "$target" | tr '\n' ' ' | sed 's/\\/\\\\/g; s/"/\\"/g')
|
|
|
|
printf '{"command":"%s","total":%d,"pid":%d,"started_at":%d,"captain_id":"%s","parallel":%d,"target":"%s","args":"%s"}\n' \
|
|
"$cmd" "${#site_count[@]}" "$bulk_pid" "$(date +%s)" "$CAPTAIN_ID" "$parallel" "$target_escaped" "$args_escaped" \
|
|
> "$progress_meta"
|
|
> "$progress_log"
|
|
export CC_PROGRESS_LOG="$progress_log"
|
|
|
|
cleanup_progress() { rm -f "$progress_meta" "$progress_log"; rmdir "$progress_dir/${bulk_pid}.lock" 2>/dev/null; }
|
|
trap cleanup_progress EXIT
|
|
export CC_OUTPUT_LOCK="$progress_dir/${bulk_pid}.lock"
|
|
|
|
if [[ "$FLAG_LABEL" == "true" ]]; then
|
|
export CC_CMD="$cmd"
|
|
export CC_ARGS_QUOTED="$cc_args_quoted"
|
|
export CC_CAPTAIN_ID="$CAPTAIN_ID"
|
|
labeled_run() {
|
|
local site="$1"
|
|
local raw_output output exit_code
|
|
raw_output=$(eval captaincore "$CC_CMD" $CC_ARGS_QUOTED --captain-id="$CC_CAPTAIN_ID" "$site" 2>/dev/null)
|
|
exit_code=$?
|
|
# Extract content between markers, stripping SSH MOTD/banner
|
|
output=$(echo "$raw_output" | sed -n '/____CC_OUTPUT_START____/,/____CC_OUTPUT_END____/{/____CC_OUTPUT_START____/d;/____CC_OUTPUT_END____/d;p;}' | sed '/^$/d')
|
|
if [[ -n "$output" ]]; then
|
|
while ! mkdir "$CC_OUTPUT_LOCK" 2>/dev/null; do sleep 0.05; done
|
|
printf "${COLOR_GREEN}== %s ==${COLOR_NORMAL}\n%s\n\n" "$site" "$output"
|
|
rmdir "$CC_OUTPUT_LOCK"
|
|
fi
|
|
printf '%s %d %d\n' "$site" "$exit_code" "$(date +%s)" >> "$CC_PROGRESS_LOG"
|
|
}
|
|
export -f labeled_run
|
|
export COLOR_GREEN COLOR_NORMAL
|
|
printf '%s\n' $target | xargs -P "$parallel" -n 1 bash -c 'labeled_run "$@"' _
|
|
else
|
|
export CC_CMD="$cmd"
|
|
export CC_ARGS_QUOTED="$cc_args_quoted"
|
|
export CC_CAPTAIN_ID="$CAPTAIN_ID"
|
|
tracked_run() {
|
|
local site="$1"
|
|
eval captaincore "$CC_CMD" $CC_ARGS_QUOTED --captain-id="$CC_CAPTAIN_ID" "$site" 2>&1
|
|
printf '%s %d %d\n' "$site" "$?" "$(date +%s)" >> "$CC_PROGRESS_LOG"
|
|
}
|
|
export -f tracked_run
|
|
printf '%s\n' $target | xargs -P "$parallel" -n 1 bash -c 'tracked_run "$@"' _
|
|
fi
|
|
|
|
}
|
|
|
|
run_command
|