From fd6b4e4cde666b27e6c32b13b3de911da94e268e Mon Sep 17 00:00:00 2001 From: whyour Date: Sun, 7 Jun 2026 13:19:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9A=E6=97=B6=E4=BB=BB=E5=8A=A1=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20work=5Fdir=20=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/data/cron.ts | 3 ++ back/loaders/db.ts | 1 + back/services/cron.ts | 3 ++ shell/otask.sh | 68 +++++++++++++++++++++++++++++++++++-- src/pages/crontab/modal.tsx | 9 +++++ 5 files changed, 82 insertions(+), 2 deletions(-) diff --git a/back/data/cron.ts b/back/data/cron.ts index 11c15b64..32b0b554 100644 --- a/back/data/cron.ts +++ b/back/data/cron.ts @@ -23,6 +23,7 @@ export class Crontab { task_after?: string; log_name?: string; allow_multiple_instances?: 1 | 0; + work_dir?: string; constructor(options: Crontab) { this.name = options.name; @@ -49,6 +50,7 @@ export class Crontab { this.task_after = options.task_after; this.log_name = options.log_name; this.allow_multiple_instances = options.allow_multiple_instances || 0; + this.work_dir = options.work_dir; } } @@ -90,4 +92,5 @@ export const CrontabModel = sequelize.define('Crontab', { task_after: DataTypes.STRING, log_name: DataTypes.STRING, allow_multiple_instances: DataTypes.NUMBER, + work_dir: DataTypes.STRING, }); diff --git a/back/loaders/db.ts b/back/loaders/db.ts index f2990611..389c2c1e 100644 --- a/back/loaders/db.ts +++ b/back/loaders/db.ts @@ -41,6 +41,7 @@ export default async () => { column: 'allow_multiple_instances', type: 'NUMBER', }, + { table: 'Crontabs', column: 'work_dir', type: 'VARCHAR(255)' }, { table: 'Envs', column: 'isPinned', type: 'NUMBER' }, { table: 'Envs', column: 'labels', type: 'JSON' }, ]; diff --git a/back/services/cron.ts b/back/services/cron.ts index 866ffb21..798ef37a 100644 --- a/back/services/cron.ts +++ b/back/services/cron.ts @@ -678,6 +678,9 @@ export default class CronService { .replace(/;? *\n/g, ';') .trim()}' `; } + if (tab.work_dir) { + commandVariable += `work_dir='${tab.work_dir.replace(/'/g, "'\\''")}' `; + } const crontab_job_string = `${commandVariable}${command}`; return crontab_job_string; diff --git a/shell/otask.sh b/shell/otask.sh index 45a25dc4..9783c29c 100755 --- a/shell/otask.sh +++ b/shell/otask.sh @@ -96,6 +96,26 @@ append_node_dependency_path() { enter_script_workdir() { local use_dot_prefix="$1" + # 如果定时任务显式指定了工作目录,优先使用 + if [[ -n "${work_dir:=}" ]]; then + if [[ "${work_dir}" == /* ]]; then + cd "${work_dir}" + else + cd "${dir_scripts}/${work_dir}" + fi + # 仍然处理 file_param 中的路径前缀,确保命令路径正确 + if [[ ${file_param} =~ "/" ]]; then + local script_name="${file_param##*/}" + if [[ "${use_dot_prefix}" == "true" ]]; then + file_param="./${script_name}" + else + file_param="${script_name}" + fi + fi + return + fi + + # 自动检测:从 file_param 中提取脚本所在目录 cd $dir_scripts if [[ ${file_param} =~ "/" ]]; then local script_dir="${file_param%/*}" @@ -210,9 +230,53 @@ run_designated() { run_else() { local file_param="$1" - enter_script_workdir true + # 判断 file_param 本身是否是脚本文件 + local is_file_script="false" + if [[ "$file_param" == *.js || "$file_param" == *.mjs || + "$file_param" == *.py || "$file_param" == *.pyc || + "$file_param" == *.sh || "$file_param" == *.ts ]]; then + is_file_script="true" + fi - shift + if [[ "$is_file_script" != "true" ]]; then + # file_param 不是脚本,从后续参数中查找脚本路径来确定工作目录 + local script_for_dir="" + for arg in "$@"; do + if [[ "$arg" == *.js || "$arg" == *.mjs || + "$arg" == *.py || "$arg" == *.pyc || + "$arg" == *.sh || "$arg" == *.ts ]]; then + script_for_dir="$arg" + break + fi + done + + if [[ -n "$script_for_dir" ]]; then + local saved_file_param="$file_param" + file_param="$script_for_dir" + enter_script_workdir true + local adjusted_script="$file_param" + file_param="$saved_file_param" + + shift + local new_args=() + for arg in "$@"; do + if [[ "$arg" == "$script_for_dir" ]]; then + new_args+=("$adjusted_script") + else + new_args+=("$arg") + fi + done + set -- "${new_args[@]}" + else + # 没有找到脚本参数,只 cd 到 scripts 目录 + enter_script_workdir true + shift + fi + else + # file_param 本身就是脚本,直接用 enter_script_workdir 处理 + enter_script_workdir true + shift + fi clear_non_sh_env $timeoutCmd $which_program $file_param "$@" diff --git a/src/pages/crontab/modal.tsx b/src/pages/crontab/modal.tsx index f4848ff8..dfe9fa21 100644 --- a/src/pages/crontab/modal.tsx +++ b/src/pages/crontab/modal.tsx @@ -229,6 +229,15 @@ const CronModal = ({ maxLength={200} /> + + +