mirror of
https://github.com/whyour/qinglong.git
synced 2026-10-16 22:35:32 +08:00
fix: improve shell compatibility and process cleanup
Preserve literal paths and arguments, use portable command options, validate startup configuration, and retain scripts when downloads fail. Ensure task watchdogs and process groups are cleaned up across Bash versions, with compatibility and process lifecycle regression coverage.
This commit is contained in:
+3
-3
@@ -1,12 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
create_token() {
|
||||
local token_command="ts-node-transpile-only ${dir_root}/back/token.ts"
|
||||
local token_file="${dir_root}/static/build/token.js"
|
||||
if [[ -f $token_file ]]; then
|
||||
token_command="node ${token_file}"
|
||||
__ql_token__=$(node "$token_file")
|
||||
else
|
||||
__ql_token__=$(ts-node-transpile-only "${dir_root}/back/token.ts")
|
||||
fi
|
||||
__ql_token__=$(eval "$token_command")
|
||||
}
|
||||
|
||||
get_token() {
|
||||
|
||||
+30
-28
@@ -39,16 +39,16 @@ random_delay() {
|
||||
gen_array_scripts() {
|
||||
local dir_current=$(pwd)
|
||||
local i="-1"
|
||||
cd $dir_scripts
|
||||
for file in $(ls); do
|
||||
cd "$dir_scripts" || return $?
|
||||
for file in *; do
|
||||
if [[ -f $file ]] && [[ $file == *.js && $file != sendNotify.js ]]; then
|
||||
let i++
|
||||
array_scripts[i]=$(echo "$file" | perl -pe "s|$dir_scripts/||g")
|
||||
array_scripts_name[i]=$(grep "new Env" $file | awk -F "\(" '{print $2}' | awk -F "\)" '{print $1}' | sed 's:.*\('\''\|"\)\([^"'\'']*\)\('\''\|"\).*:\2:' | sed 's:"::g' | sed "s:'::g" | head -1)
|
||||
array_scripts[i]=$file
|
||||
array_scripts_name[i]=$(grep -- "new Env" "$file" | awk -F "\(" '{print $2}' | awk -F "\)" '{print $1}' | sed 's:.*\('\''\|"\)\([^"'\'']*\)\('\''\|"\).*:\2:' | sed 's:"::g' | sed "s:'::g" | head -1)
|
||||
[[ -z ${array_scripts_name[i]} ]] && array_scripts_name[i]="<未识别出活动名称>"
|
||||
fi
|
||||
done
|
||||
cd $dir_current
|
||||
cd "$dir_current" || return $?
|
||||
}
|
||||
|
||||
## 使用说明
|
||||
@@ -72,11 +72,11 @@ usage() {
|
||||
## run nohup,$1:文件名,不含路径,带后缀
|
||||
run_nohup() {
|
||||
local file_name=$1
|
||||
nohup node $file_name &>$log_path &
|
||||
nohup node "$file_name" &>"$log_path" &
|
||||
}
|
||||
|
||||
env_str_to_array() {
|
||||
. $file_env
|
||||
. "$file_env"
|
||||
local IFS="&"
|
||||
read -ra array <<<"${!env_param}"
|
||||
array_length=${#array[@]}
|
||||
@@ -122,7 +122,7 @@ enter_script_workdir() {
|
||||
_target_dir="${dir_scripts}/${work_dir}"
|
||||
fi
|
||||
if [[ -d "${_target_dir}" ]]; then
|
||||
cd "${_target_dir}"
|
||||
cd "${_target_dir}" || return $?
|
||||
if [[ ${file_param} =~ "/" ]]; then
|
||||
local script_name="${file_param##*/}"
|
||||
if [[ "${use_dot_prefix}" == "true" ]]; then
|
||||
@@ -136,13 +136,13 @@ enter_script_workdir() {
|
||||
t '警告:工作目录不存在 %s' "${_target_dir}"
|
||||
fi
|
||||
|
||||
cd $dir_scripts
|
||||
cd "$dir_scripts" || return $?
|
||||
if [[ ${file_param} =~ "/" ]]; then
|
||||
local script_dir="${file_param%/*}"
|
||||
local script_name="${file_param##*/}"
|
||||
|
||||
if [[ -d ${script_dir} ]]; then
|
||||
cd ${script_dir}
|
||||
cd "$script_dir" || return $?
|
||||
if [[ "${use_dot_prefix}" == "true" ]]; then
|
||||
file_param="./${script_name}"
|
||||
else
|
||||
@@ -159,12 +159,12 @@ run_normal() {
|
||||
random_delay "$file_param"
|
||||
fi
|
||||
|
||||
enter_script_workdir
|
||||
enter_script_workdir || return $?
|
||||
|
||||
if [[ $isJsOrPythonFile == 'false' ]]; then
|
||||
clear_non_sh_env
|
||||
fi
|
||||
run_task_command $which_program $file_param "${script_params[@]}"
|
||||
run_task_command ${which_program:+"$which_program"} "$file_param" "${script_params[@]}"
|
||||
}
|
||||
|
||||
handle_env_split() {
|
||||
@@ -175,7 +175,7 @@ handle_env_split() {
|
||||
env_str_to_array
|
||||
local tempArr=$(echo $num_param | sed "s/-max/-${array_length}/g" | sed "s/max-/${array_length}-/g" | perl -pe "s|(\d+)(-\|~\|_)(\d+)|{\1..\3}|g")
|
||||
local runArr=($(eval echo $tempArr))
|
||||
array_run=($(awk -v RS=' ' '!a[$1]++' <<<${runArr[@]}))
|
||||
array_run=($(awk -v RS=' ' '!a[$1]++' <<<"${runArr[*]}"))
|
||||
}
|
||||
|
||||
## 并发执行时,设定的 RandomDelay 不会生效,即所有任务立即执行
|
||||
@@ -192,10 +192,10 @@ run_concurrent() {
|
||||
time=$(date "+$mtime_format")
|
||||
single_log_time=$(format_log_time "$mtime_format" "$time")
|
||||
|
||||
enter_script_workdir
|
||||
enter_script_workdir || return $?
|
||||
|
||||
local j=0
|
||||
for i in ${array_run[@]}; do
|
||||
for i in "${array_run[@]}"; do
|
||||
single_log_path="$dir_log/$log_dir/${single_log_time}_$((j + 1)).log"
|
||||
let j++
|
||||
|
||||
@@ -203,16 +203,16 @@ run_concurrent() {
|
||||
export "${env_param}=${array[$i - 1]}"
|
||||
clear_non_sh_env
|
||||
fi
|
||||
eval envParam="${env_param}" numParam="${i}" run_task_command $which_program $file_param "${script_params[@]}" &>$single_log_path &
|
||||
envParam="${env_param}" numParam="${i}" run_task_command ${which_program:+"$which_program"} "$file_param" "${script_params[@]}" &>"$single_log_path" &
|
||||
done
|
||||
|
||||
wait
|
||||
local k=0
|
||||
for i in ${array_run[@]}; do
|
||||
for i in "${array_run[@]}"; do
|
||||
single_log_path="$dir_log/$log_dir/${single_log_time}_$((k + 1)).log"
|
||||
let k++
|
||||
cat $single_log_path
|
||||
[[ -f $single_log_path ]] && rm -f $single_log_path
|
||||
cat "$single_log_path"
|
||||
[[ -f $single_log_path ]] && rm -f "$single_log_path"
|
||||
done
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ run_designated() {
|
||||
|
||||
if [[ $isJsOrPythonFile == 'false' ]]; then
|
||||
local n=0
|
||||
for i in ${array_run[@]}; do
|
||||
for i in "${array_run[@]}"; do
|
||||
array_str[n]=${array[$i - 1]}
|
||||
let n++
|
||||
done
|
||||
@@ -241,9 +241,9 @@ run_designated() {
|
||||
clear_non_sh_env
|
||||
fi
|
||||
|
||||
enter_script_workdir
|
||||
enter_script_workdir || return $?
|
||||
|
||||
envParam="${env_param}" numParam="${num_param}" run_task_command $which_program $file_param "${script_params[@]}"
|
||||
envParam="${env_param}" numParam="${num_param}" run_task_command ${which_program:+"$which_program"} "$file_param" "${script_params[@]}"
|
||||
}
|
||||
|
||||
## 运行其他命令
|
||||
@@ -273,7 +273,7 @@ run_else() {
|
||||
if [[ -n "$script_for_dir" ]]; then
|
||||
local saved_file_param="$file_param"
|
||||
file_param="$script_for_dir"
|
||||
enter_script_workdir true
|
||||
enter_script_workdir true || return $?
|
||||
local adjusted_script="$file_param"
|
||||
file_param="$saved_file_param"
|
||||
|
||||
@@ -289,17 +289,17 @@ run_else() {
|
||||
set -- "${new_args[@]}"
|
||||
else
|
||||
# 没有找到脚本参数,只 cd 到 scripts 目录
|
||||
enter_script_workdir true
|
||||
enter_script_workdir true || return $?
|
||||
shift
|
||||
fi
|
||||
else
|
||||
# file_param 本身就是脚本,直接用 enter_script_workdir 处理
|
||||
enter_script_workdir true
|
||||
enter_script_workdir true || return $?
|
||||
shift
|
||||
fi
|
||||
|
||||
clear_non_sh_env
|
||||
run_task_command $which_program $file_param "$@"
|
||||
run_task_command ${which_program:+"$which_program"} "$file_param" "$@"
|
||||
}
|
||||
|
||||
check_file() {
|
||||
@@ -313,12 +313,14 @@ check_file() {
|
||||
export PREV_NODE_OPTIONS="${NODE_OPTIONS:=}"
|
||||
export PREV_PYTHONPATH="${PYTHONPATH:=}"
|
||||
if [[ $1 == *.js ]] || [[ $1 == *.ts ]] || [[ $1 == *.mjs ]]; then
|
||||
export NODE_OPTIONS="-r ${file_preload_js} ${NODE_OPTIONS}"
|
||||
local preload_path=${file_preload_js//\\/\\\\}
|
||||
preload_path=${preload_path//\"/\\\"}
|
||||
export NODE_OPTIONS="-r \"${preload_path}\" ${NODE_OPTIONS}"
|
||||
else
|
||||
export PYTHONPATH="${dir_preload}:${dir_config}:${PYTHONPATH}"
|
||||
fi
|
||||
else
|
||||
. $file_env
|
||||
. "$file_env"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
+8
-2
@@ -1,4 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# BSD grep accepts \d as a digit; GNU grep treats it as a literal d.
|
||||
string=$(awk '$1 == "version:" { print $2; exit }' version.yaml) || exit 1
|
||||
if [[ ! $string =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[[:alnum:].-]+)?(\+[[:alnum:].-]+)?$ ]]; then
|
||||
printf 'Invalid release version: %s\n' "$string" >&2
|
||||
exit 1
|
||||
fi
|
||||
version="v$string"
|
||||
echo -e "开始发布"
|
||||
|
||||
echo -e "切换master分支"
|
||||
@@ -9,8 +17,6 @@ git push --set-upstream origin master -f
|
||||
echo -e "更新cdn文件"
|
||||
ts-node-transpile-only sample/tool.ts
|
||||
|
||||
string=$(cat version.yaml | grep "version" | egrep "[^ ]*" -o | egrep "\d\.*")
|
||||
version="v$string"
|
||||
echo -e "当前版本$version"
|
||||
|
||||
echo -e "删除已经存在的本地tag"
|
||||
|
||||
+21
-16
@@ -3,42 +3,47 @@
|
||||
days=$1
|
||||
|
||||
remove_js_log() {
|
||||
local log_full_path_list=$(find $dir_log -name "*.log")
|
||||
local diff_time
|
||||
for log in $log_full_path_list; do
|
||||
local log_date=$(echo $log | awk -F "/" '{print $NF}' | cut -c1-10)
|
||||
local log log_date log_timestamp diff_time log_path encoded_path result
|
||||
local now
|
||||
now=$(date +%s) || return $?
|
||||
while IFS= read -r -d '' log; do
|
||||
log_date=${log##*/}
|
||||
log_date=${log_date:0:10}
|
||||
if ! [[ $log_date =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
|
||||
if [[ $is_macos -eq 1 ]]; then
|
||||
log_date=$(stat -f %Sm -t "%Y-%m-%d" "$log")
|
||||
log_date=$(stat -f %Sm -t "%Y-%m-%d" "$log") || continue
|
||||
else
|
||||
log_date=$(stat -c %y "$log" | cut -d ' ' -f 1)
|
||||
fi
|
||||
fi
|
||||
if [[ $is_macos -eq 1 ]]; then
|
||||
diff_time=$(($(date +%s) - $(date -j -f "%Y-%m-%d" "$log_date" +%s)))
|
||||
# BSD date otherwise fills the omitted time from the current clock.
|
||||
log_timestamp=$(date -j -f "%Y-%m-%d %H:%M:%S" "$log_date 00:00:00" +%s) || continue
|
||||
else
|
||||
diff_time=$(($(date +%s) - $(date +%s -d "$log_date")))
|
||||
log_timestamp=$(date +%s -d "$log_date") || continue
|
||||
fi
|
||||
diff_time=$((now - log_timestamp))
|
||||
if [[ $diff_time -gt $((${days} * 86400)) ]]; then
|
||||
local log_path=$(echo "$log" | sed "s,${dir_log}/,,g")
|
||||
local result=$(find_cron_api "log_path=$log_path")
|
||||
log_path=${log#"$dir_log"/}
|
||||
encoded_path=$(printf '%s' "$log_path" | jq -sRr @uri) || continue
|
||||
result=$(find_cron_api "log_path=$encoded_path")
|
||||
t '查询文件 %s' "$log_path"
|
||||
if [[ -z $result ]]; then
|
||||
t '删除中~'
|
||||
rm -vf $log
|
||||
rm -vf -- "$log"
|
||||
else
|
||||
t '正在被 %s 使用,跳过~' "$result"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
done < <(find "$dir_log" -type f -name "*.log" -print0)
|
||||
}
|
||||
|
||||
remove_empty_dir() {
|
||||
cd $dir_log
|
||||
for dir in $(ls); do
|
||||
if [[ -d $dir ]] && [[ -z $(ls $dir) ]]; then
|
||||
rm -rf $dir
|
||||
fi
|
||||
local dir
|
||||
for dir in "$dir_log"/*/ "$dir_log"/.[!.]*/ "$dir_log"/..?*/; do
|
||||
[[ -d "$dir" ]] || continue
|
||||
# rmdir also protects a directory populated between discovery and removal.
|
||||
rmdir -- "$dir" 2>/dev/null || true
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
+45
-41
@@ -77,16 +77,19 @@ load_ql_envs() {
|
||||
}
|
||||
|
||||
import_config() {
|
||||
[[ -f $file_config_user ]] && . $file_config_user
|
||||
[[ -f $dir_preload/lang_env.sh ]] && . $dir_preload/lang_env.sh
|
||||
[[ -f $file_config_user ]] && . "$file_config_user"
|
||||
[[ -f $dir_preload/lang_env.sh ]] && . "$dir_preload/lang_env.sh"
|
||||
|
||||
# 加载语言包(bash 4+ 支持 declare -A,不兼容时回退输出中文 key)
|
||||
# Language packs need global associative arrays (Bash 4.2+). Probe the
|
||||
# capability so Bash 3.2 and 4.0/4.1 retain the Chinese-key fallback.
|
||||
local lang=${QL_LANG:-zh}
|
||||
local lang_file="$dir_shell/lang/${lang}.sh"
|
||||
if [[ ${BASH_VERSINFO[0]} -ge 4 ]] && [[ -f $lang_file ]]; then
|
||||
. $lang_file
|
||||
elif [[ ${BASH_VERSINFO[0]} -ge 4 ]]; then
|
||||
. "$dir_shell/lang/zh.sh"
|
||||
if (declare -gA __ql_lang_probe) 2>/dev/null; then
|
||||
if [[ -f $lang_file ]]; then
|
||||
. "$lang_file"
|
||||
elif [[ -f "$dir_shell/lang/zh.sh" ]]; then
|
||||
. "$dir_shell/lang/zh.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
load_ql_envs
|
||||
@@ -132,7 +135,7 @@ unset_proxy() {
|
||||
make_dir() {
|
||||
local dir=$1
|
||||
if [[ ! -d $dir ]]; then
|
||||
mkdir -p $dir
|
||||
mkdir -p -- "$dir"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -176,57 +179,57 @@ define_cmd() {
|
||||
}
|
||||
|
||||
fix_config() {
|
||||
make_dir $dir_tmp
|
||||
make_dir $dir_static
|
||||
make_dir $dir_data
|
||||
make_dir $dir_config
|
||||
make_dir $dir_log
|
||||
make_dir $dir_db
|
||||
make_dir $dir_scripts
|
||||
make_dir $dir_list_tmp
|
||||
make_dir $dir_repo
|
||||
make_dir $dir_raw
|
||||
make_dir $dir_update_log
|
||||
make_dir $dir_dep
|
||||
make_dir "$dir_tmp"
|
||||
make_dir "$dir_static"
|
||||
make_dir "$dir_data"
|
||||
make_dir "$dir_config"
|
||||
make_dir "$dir_log"
|
||||
make_dir "$dir_db"
|
||||
make_dir "$dir_scripts"
|
||||
make_dir "$dir_list_tmp"
|
||||
make_dir "$dir_repo"
|
||||
make_dir "$dir_raw"
|
||||
make_dir "$dir_update_log"
|
||||
make_dir "$dir_dep"
|
||||
|
||||
if [[ ! -s $file_config_user ]]; then
|
||||
cp -f $file_config_sample $file_config_user
|
||||
cp -f "$file_config_sample" "$file_config_user"
|
||||
fi
|
||||
|
||||
if [[ ! -f $file_task_before ]]; then
|
||||
cp -f $file_task_sample $file_task_before
|
||||
cp -f "$file_task_sample" "$file_task_before"
|
||||
fi
|
||||
|
||||
if [[ ! -f $file_task_after ]]; then
|
||||
cp -f $file_task_sample $file_task_after
|
||||
cp -f "$file_task_sample" "$file_task_after"
|
||||
fi
|
||||
|
||||
if [[ ! -f $file_extra_shell ]]; then
|
||||
cp -f $file_extra_sample $file_extra_shell
|
||||
cp -f "$file_extra_sample" "$file_extra_shell"
|
||||
fi
|
||||
|
||||
if [[ ! -s $file_notify_py ]]; then
|
||||
cp -f $file_notify_py_sample $file_notify_py
|
||||
cp -f "$file_notify_py_sample" "$file_notify_py"
|
||||
fi
|
||||
|
||||
if [[ ! -s $file_notify_js ]]; then
|
||||
cp -f $file_notify_js_sample $file_notify_js
|
||||
cp -f "$file_notify_js_sample" "$file_notify_js"
|
||||
fi
|
||||
|
||||
if [[ ! -s $file_test_js ]]; then
|
||||
cp -f $file_test_js_sample $file_test_js
|
||||
cp -f "$file_test_js_sample" "$file_test_js"
|
||||
fi
|
||||
|
||||
if [[ ! -s $file_test_py ]]; then
|
||||
cp -f $file_test_py_sample $file_test_py
|
||||
cp -f "$file_test_py_sample" "$file_test_py"
|
||||
fi
|
||||
|
||||
if [[ ! -s $dep_notify_js ]]; then
|
||||
cp -f $file_notify_js_sample $dep_notify_js
|
||||
cp -f "$file_notify_js_sample" "$dep_notify_js"
|
||||
fi
|
||||
|
||||
if [[ ! -s $dep_notify_py ]]; then
|
||||
cp -f $file_notify_py_sample $dep_notify_py
|
||||
cp -f "$file_notify_py_sample" "$dep_notify_py"
|
||||
fi
|
||||
|
||||
}
|
||||
@@ -246,17 +249,17 @@ npm_install_2() {
|
||||
local dir_current=$(pwd)
|
||||
local dir_work=$1
|
||||
|
||||
cd $dir_work
|
||||
cd "$dir_work" || return $?
|
||||
t '安装 %s 依赖包...\n' "$dir_work"
|
||||
npm_install_sub
|
||||
cd $dir_current
|
||||
cd "$dir_current" || return $?
|
||||
}
|
||||
|
||||
diff_and_copy() {
|
||||
local copy_source=$1
|
||||
local copy_to=$2
|
||||
if [[ ! -s $copy_to ]] || [[ $(diff $copy_source $copy_to) ]]; then
|
||||
cp -f $copy_source $copy_to
|
||||
if [[ ! -s $copy_to ]] || [[ $(diff "$copy_source" "$copy_to") ]]; then
|
||||
cp -f "$copy_source" "$copy_to"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -265,12 +268,13 @@ git_clone_scripts() {
|
||||
local dir="$2"
|
||||
local branch="$3"
|
||||
local proxy="$4"
|
||||
[[ $branch ]] && local part_cmd="-b $branch "
|
||||
local branch_args=()
|
||||
[[ $branch ]] && branch_args=(-b "$branch")
|
||||
t '开始拉取仓库 %s 到 %s\n' "${uniq_path}" "$dir"
|
||||
|
||||
set_proxy "$proxy"
|
||||
|
||||
git clone -q --depth=1 $part_cmd $url $dir
|
||||
git clone -q --depth=1 "${branch_args[@]}" "$url" "$dir"
|
||||
exit_status=$?
|
||||
|
||||
unset_proxy
|
||||
@@ -283,7 +287,7 @@ random_range() {
|
||||
}
|
||||
|
||||
delete_pm2() {
|
||||
cd $dir_root
|
||||
cd "$dir_root" || return $?
|
||||
# Try to delete PM2 processes, but don't fail if PM2 is not available
|
||||
pm2 delete ecosystem.config.js 2>/dev/null || true
|
||||
# Also try to kill any directly spawned node processes
|
||||
@@ -291,7 +295,7 @@ delete_pm2() {
|
||||
}
|
||||
|
||||
reload_pm2() {
|
||||
cd $dir_root
|
||||
cd "$dir_root" || return $?
|
||||
restore_env_vars
|
||||
|
||||
# Try to start PM2, but handle failures gracefully
|
||||
@@ -310,7 +314,7 @@ reload_pm2() {
|
||||
if [[ "$QL_CONTAINER" == "true" ]]; then
|
||||
nohup node static/build/app.js >/proc/1/fd/1 2>/proc/1/fd/2 &
|
||||
else
|
||||
nohup node static/build/app.js >$dir_log/qinglong.log 2>&1 &
|
||||
nohup node static/build/app.js >"$dir_log/qinglong.log" 2>&1 &
|
||||
fi
|
||||
local node_pid=$!
|
||||
|
||||
@@ -398,7 +402,7 @@ handle_task_start() {
|
||||
}
|
||||
|
||||
run_task_before() {
|
||||
. $file_task_before "$@"
|
||||
. "$file_task_before" "$@"
|
||||
|
||||
if [[ ${task_before:=} ]]; then
|
||||
t '执行前置命令\n'
|
||||
@@ -408,7 +412,7 @@ run_task_before() {
|
||||
}
|
||||
|
||||
run_task_after() {
|
||||
. $file_task_after "$@"
|
||||
. "$file_task_after" "$@"
|
||||
|
||||
if [[ ${task_after:=} ]]; then
|
||||
t '\n执行后置命令\n'
|
||||
|
||||
@@ -63,12 +63,12 @@ reload_pm2
|
||||
if [[ $command != "reload" ]]; then
|
||||
if [[ $AutoStartBot == true ]]; then
|
||||
log_with_style "INFO" "🤖 4. 启动 bot..."
|
||||
nohup ql bot >$dir_log/bot.log 2>&1 &
|
||||
nohup ql bot >"$dir_log/bot.log" 2>&1 &
|
||||
fi
|
||||
|
||||
if [[ $EnableExtraShell == true ]]; then
|
||||
log_with_style "INFO" "🛠️ 5. 执行自定义脚本..."
|
||||
nohup ql extra >$dir_log/extra.log 2>&1 &
|
||||
nohup ql extra >"$dir_log/extra.log" 2>&1 &
|
||||
fi
|
||||
|
||||
pm2 startup
|
||||
|
||||
+30
-11
@@ -4,31 +4,46 @@
|
||||
set -e
|
||||
set -x
|
||||
|
||||
# Configuration and language helpers are not loaded during bootstrap errors.
|
||||
startup_message() {
|
||||
local format="$1"
|
||||
shift
|
||||
printf -- "$format\n" "$@" >&2
|
||||
}
|
||||
|
||||
if [[ ! $QL_DIR ]]; then
|
||||
npm_dir=$(npm root -g)
|
||||
pnpm_dir=$(pnpm root -g)
|
||||
if [[ -d "$npm_dir/@whyour/qinglong" ]]; then
|
||||
npm_dir=$(npm root -g 2>/dev/null) || npm_dir=""
|
||||
pnpm_dir=""
|
||||
if command -v pnpm >/dev/null 2>&1; then
|
||||
pnpm_dir=$(pnpm root -g 2>/dev/null) || pnpm_dir=""
|
||||
fi
|
||||
if [[ -n "$npm_dir" && -d "$npm_dir/@whyour/qinglong" ]]; then
|
||||
QL_DIR="$npm_dir/@whyour/qinglong"
|
||||
elif [[ -d "$pnpm_dir/@whyour/qinglong" ]]; then
|
||||
elif [[ -n "$pnpm_dir" && -d "$pnpm_dir/@whyour/qinglong" ]]; then
|
||||
QL_DIR="$pnpm_dir/@whyour/qinglong"
|
||||
else
|
||||
t '未找到 qinglong 模块,请先执行 npm i -g @whyour/qinglong 安装'
|
||||
startup_message '未找到 qinglong 模块,请先执行 npm i -g @whyour/qinglong 安装'
|
||||
fi
|
||||
|
||||
if [[ $QL_DIR ]]; then
|
||||
t '请先手动设置 export QL_DIR=%s,环境变量,并手动添加到系统环境变量,然后再次执行命令 qinglong 启动服务' "$QL_DIR"
|
||||
startup_message '请先手动设置 export QL_DIR=%s,环境变量,并手动添加到系统环境变量,然后再次执行命令 qinglong 启动服务' "$QL_DIR"
|
||||
fi
|
||||
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $QL_DIR != /* ]]; then
|
||||
startup_message 'QL_DIR 必须为绝对路径,例如 /ql'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ! $QL_DATA_DIR ]]; then
|
||||
t '请先手动设置数据存储目录 export QL_DATA_DIR 环境变量,目录必须以斜杠开头的绝对路径,并且以 /data 结尾,例如 /ql/data 并手动添加到系统环境变量'
|
||||
startup_message '请先手动设置数据存储目录 export QL_DATA_DIR 环境变量,目录必须以斜杠开头的绝对路径,并且以 /data 结尾,例如 /ql/data 并手动添加到系统环境变量'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $QL_DATA_DIR != */data ]]; then
|
||||
t 'QL_DATA_DIR 必须以 /data 结尾,例如 /ql/data,如果有历史数据,请新建 data 目录,把历史数据放到 data 目录中'
|
||||
if [[ $QL_DATA_DIR != /*/data ]]; then
|
||||
startup_message 'QL_DATA_DIR 必须为以 /data 结尾的绝对路径,例如 /ql/data,如果有历史数据,请新建 data 目录,把历史数据放到 data 目录中'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -38,7 +53,11 @@ if [[ $command != "reload" ]]; then
|
||||
# 安装依赖
|
||||
os_name="${QL_OS_TYPE:-}"
|
||||
if [ -z "$os_name" ]; then
|
||||
os_name=$(source /etc/os-release && echo "$ID")
|
||||
if [[ -r /etc/os-release ]]; then
|
||||
os_name=$(source /etc/os-release && echo "$ID")
|
||||
else
|
||||
os_name=$(uname -s)
|
||||
fi
|
||||
fi
|
||||
|
||||
# 非 root 用户使用 sudo
|
||||
@@ -69,7 +88,7 @@ if [[ $command != "reload" ]]; then
|
||||
$SUDO apt-get install -y git curl wget tzdata perl openssl jq nginx procps netcat-openbsd openssh-client
|
||||
;;
|
||||
*)
|
||||
t '暂不支持此系统部署 %s' "$os_name"
|
||||
startup_message '暂不支持此系统部署 %s' "$os_name"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
+17
-9
@@ -36,7 +36,10 @@ run_task_command() {
|
||||
) &
|
||||
timeout_watcher=$!
|
||||
set +m
|
||||
trap '{
|
||||
finish_task_timeout() {
|
||||
local code="$1"
|
||||
trap - EXIT
|
||||
{
|
||||
# Bash can report a killed job between any two cleanup commands.
|
||||
# Keep redirection active until the watchdog is fully reaped.
|
||||
kill -KILL -- "-$timeout_watcher" || :
|
||||
@@ -45,14 +48,19 @@ run_task_command() {
|
||||
if kill -TERM -- "-$timeout_worker" 2>/dev/null; then sleep 0.1; fi
|
||||
kill -KILL -- "-$timeout_worker" 2>/dev/null || :
|
||||
wait "$timeout_worker" 2>/dev/null || :
|
||||
rm -rf -- "$timeout_dir"' EXIT
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
trap 'exit 129' HUP
|
||||
trap 'exit 131' QUIT
|
||||
rm -rf -- "$timeout_dir"
|
||||
exit "$code"
|
||||
}
|
||||
# Older Bash can omit a subshell's EXIT trap in sourced pipelines. Call
|
||||
# cleanup explicitly on completion/signals, retaining EXIT as a fallback.
|
||||
trap 'finish_task_timeout "$?"' EXIT
|
||||
trap 'finish_task_timeout 130' INT
|
||||
trap 'finish_task_timeout 143' TERM
|
||||
trap 'finish_task_timeout 129' HUP
|
||||
trap 'finish_task_timeout 131' QUIT
|
||||
# Do not clean up a timer whose process group has not initialized yet.
|
||||
while [[ ! -f "$timeout_dir/ready" ]]; do
|
||||
kill -0 "$timeout_watcher" 2>/dev/null || exit 125
|
||||
kill -0 "$timeout_watcher" 2>/dev/null || finish_task_timeout 125
|
||||
sleep 0.001
|
||||
done
|
||||
wait "$timeout_worker"
|
||||
@@ -60,8 +68,8 @@ run_task_command() {
|
||||
if [[ -f "$timeout_dir/expired" ]]; then
|
||||
# Let the timer finish escalation even if the task leader exited first.
|
||||
wait "$timeout_watcher" 2>/dev/null || :
|
||||
exit 124
|
||||
finish_task_timeout 124
|
||||
fi
|
||||
exit "$timeout_code"
|
||||
finish_task_timeout "$timeout_code"
|
||||
)
|
||||
}
|
||||
|
||||
+7
-7
@@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
dir_shell=$QL_DIR/shell
|
||||
. $dir_shell/share.sh
|
||||
. $dir_shell/api.sh
|
||||
. $dir_shell/task-timeout.sh
|
||||
. "$dir_shell/share.sh"
|
||||
. "$dir_shell/api.sh"
|
||||
. "$dir_shell/task-timeout.sh"
|
||||
|
||||
trap 'single_hanle SIGINT' INT
|
||||
trap 'single_hanle SIGTERM' TERM
|
||||
@@ -48,7 +48,7 @@ handle_log_path() {
|
||||
fi
|
||||
|
||||
if [[ -z ${ID:=} ]]; then
|
||||
ID=$(cat $list_crontab_user | grep -E "$cmd_task.* $file_param" | perl -pe "s|.*ID=(.*) $cmd_task.* $file_param\.*|\1|" | head -1 | awk -F " " '{print $1}')
|
||||
ID=$(cat "$list_crontab_user" | grep -E "$cmd_task.* $file_param" | perl -pe "s|.*ID=(.*) $cmd_task.* $file_param\.*|\1|" | head -1 | awk -F " " '{print $1}')
|
||||
fi
|
||||
local suffix=""
|
||||
if [[ ! -z $ID ]]; then
|
||||
@@ -89,10 +89,10 @@ handle_log_path() {
|
||||
log_path="$real_log_path"
|
||||
fi
|
||||
|
||||
cmd="2>&1 | tee -a $dir_log/$log_path"
|
||||
printf -v cmd '2>&1 | tee -a %q' "$dir_log/$log_path"
|
||||
make_dir "$dir_log/$log_dir"
|
||||
if [[ "${no_tee:=}" == "true" ]]; then
|
||||
cmd=">> $dir_log/$log_path 2>&1"
|
||||
printf -v cmd '>> %q 2>&1' "$dir_log/$log_path"
|
||||
fi
|
||||
|
||||
if [[ "${real_time:=}" == "true" ]]; then
|
||||
@@ -162,5 +162,5 @@ define_program "${task_shell_params[@]}"
|
||||
handle_log_path "${task_shell_params[@]}"
|
||||
init_begin_time
|
||||
|
||||
eval . $dir_shell/otask.sh "$cmd"
|
||||
eval '. "$dir_shell/otask.sh"' "$cmd"
|
||||
exit 0
|
||||
|
||||
+23
-17
@@ -1,10 +1,10 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
dir_shell=$QL_DIR/shell
|
||||
. $dir_shell/share.sh
|
||||
. $dir_shell/api.sh
|
||||
. "$dir_shell/share.sh"
|
||||
. "$dir_shell/api.sh"
|
||||
load_ql_envs
|
||||
. $dir_shell/env.sh
|
||||
. "$dir_shell/env.sh"
|
||||
|
||||
send_mark=$dir_shell/send_mark
|
||||
|
||||
@@ -165,17 +165,20 @@ update_raw() {
|
||||
t '开始下载:%s 保存路径:%s\n' "${raw_url}" "$dir_raw/${raw_file_name}"
|
||||
|
||||
set_proxy "$proxy"
|
||||
wget -q --no-check-certificate -O "$dir_raw/${raw_file_name}.new" ${raw_url}
|
||||
exit_status=$?
|
||||
if wget -q --no-check-certificate -O "$dir_raw/${raw_file_name}.new" "$raw_url"; then
|
||||
exit_status=0
|
||||
else
|
||||
exit_status=$?
|
||||
fi
|
||||
unset_proxy
|
||||
|
||||
if [[ $? -eq 0 ]]; then
|
||||
mv "$dir_raw/${raw_file_name}.new" "$dir_raw/${raw_file_name}"
|
||||
if [[ $exit_status -eq 0 ]]; then
|
||||
mv "$dir_raw/${raw_file_name}.new" "$dir_raw/${raw_file_name}" || return $?
|
||||
t '下载 %s 成功...\n' "${raw_file_name}"
|
||||
cd $dir_raw
|
||||
cd "$dir_raw" || return $?
|
||||
local filename="raw_${raw_file_name}"
|
||||
local cron_id=$(cat $list_crontab_user | grep -E "$cmd_task.* $filename" | perl -pe "s|.*ID=(.*) $cmd_task.* $filename\.*|\1|" | head -1 | awk -F " " '{print $1}')
|
||||
cp -f $raw_file_name $dir_scripts/${filename}
|
||||
local cron_id=$(cat "$list_crontab_user" | grep -E "$cmd_task.* $filename" | perl -pe "s|.*ID=(.*) $cmd_task.* $filename\.*|\1|" | head -1 | awk -F " " '{print $1}')
|
||||
cp -f "$raw_file_name" "$dir_scripts/${filename}" || return $?
|
||||
if [[ -z $cron_id ]] && [[ ${autoAddCron} == true ]]; then
|
||||
cron_line=$(
|
||||
perl -ne "{
|
||||
@@ -202,6 +205,7 @@ update_raw() {
|
||||
else
|
||||
t '下载 %s 失败,保留之前正常下载的版本...\n' "${raw_file_name}"
|
||||
[[ -f "$dir_raw/${raw_file_name}.new" ]] && rm -f "$dir_raw/${raw_file_name}.new"
|
||||
return "$exit_status"
|
||||
fi
|
||||
|
||||
}
|
||||
@@ -209,7 +213,7 @@ update_raw() {
|
||||
## 调用用户自定义的extra.sh
|
||||
run_extra_shell() {
|
||||
if [[ -f $file_extra_shell ]]; then
|
||||
. $file_extra_shell
|
||||
. "$file_extra_shell"
|
||||
else
|
||||
t '%s文件不存在,跳过执行...\n' "$file_extra_shell"
|
||||
fi
|
||||
@@ -266,7 +270,8 @@ show_service_logs() {
|
||||
# Winston writes the active runtime log by date. Follow the filename so
|
||||
# size-based rotations are handled without involving the PM2 log files.
|
||||
local current_system_log="$system_log_dir/$(date +%F).log"
|
||||
tail -n 0 --retry -F "$current_system_log"
|
||||
# GNU and BSD tail both support -F; --retry is GNU-only and implied by -F.
|
||||
tail -n 0 -F "$current_system_log"
|
||||
}
|
||||
|
||||
## 脚本用法
|
||||
@@ -484,9 +489,9 @@ main() {
|
||||
local log_path="${log_dir}/${log_time}.log"
|
||||
local file_path="$dir_log/$log_path"
|
||||
|
||||
cmd="2>&1 | tee -a $file_path"
|
||||
printf -v cmd '2>&1 | tee -a %q' "$file_path"
|
||||
if [[ "$no_tee" == "true" ]]; then
|
||||
cmd=">> $file_path 2>&1"
|
||||
printf -v cmd '>> %q 2>&1' "$file_path"
|
||||
fi
|
||||
if [[ "$real_time" == "true" ]]; then
|
||||
cmd=""
|
||||
@@ -532,19 +537,20 @@ main() {
|
||||
get_uniq_path "$p2"
|
||||
if [[ -n $p2 ]]; then
|
||||
update_raw "$p2" "$p3" "$p4" "$p5"
|
||||
command_status=$?
|
||||
else
|
||||
t '命令输入错误...\n'
|
||||
eval usage $cmd
|
||||
fi
|
||||
;;
|
||||
rmlog)
|
||||
eval . $dir_shell/rmlog.sh "$p2" $cmd
|
||||
eval '. "$dir_shell/rmlog.sh" "$p2"' "$cmd"
|
||||
;;
|
||||
bot)
|
||||
eval . $dir_shell/bot.sh $cmd
|
||||
eval '. "$dir_shell/bot.sh"' "$cmd"
|
||||
;;
|
||||
check)
|
||||
eval . $dir_shell/check.sh $cmd
|
||||
eval '. "$dir_shell/check.sh"' "$cmd"
|
||||
;;
|
||||
resetlet)
|
||||
eval update_auth_config "\\\"retries\\\":0" "重置登录错误次数" $cmd
|
||||
|
||||
Reference in New Issue
Block a user