feat: 本地 uv 与托管 Python 环境管理
- 新增 uv 管理:只认程序目录副本或指定路径,支持从科大镜像下载并按 sha256 校验 - uv 环境变量(UV_*)可配置,默认相对程序目录(python / uv-cache),同时注入 uv 调用与任务进程 - 新增托管虚拟环境与 Python 版本管理:用 uv 下载解释器、预创建环境、查看已装包 - 新建任务保存时自动创建 .venv,运行期只校验、不再自动装依赖;镜像只在装包时使用 - 前端改为顶部品牌栏 + 三栏工作台,uv / 镜像源 / 虚拟环境各自独立弹窗 - 发布包构建后去掉工作区继承的低完整性标签 - 修复调度集成测试并发时误取 skipped 运行的问题
This commit is contained in:
+46
-5
@@ -201,6 +201,46 @@ async fn wait_for_run(router: &Router, run_id: i64) -> Value {
|
||||
panic!("run {run_id} did not reach a terminal state");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn python_task_without_venv_fails_with_editor_hint() {
|
||||
let app = test_app().await;
|
||||
let working_dir = app._temp_dir.path().join("bare-python");
|
||||
fs::create_dir_all(&working_dir).expect("working directory should be created");
|
||||
fs::write(working_dir.join("main.py"), "print('hi')\n").expect("script should be written");
|
||||
|
||||
let mut task = script_task(
|
||||
"bare_python",
|
||||
&working_dir.to_string_lossy(),
|
||||
"python.exe",
|
||||
"main.py",
|
||||
vec![],
|
||||
"0 0 2 * * *",
|
||||
10,
|
||||
);
|
||||
task["action"]["language"] = json!("python");
|
||||
let create_response = send_json(&app.router, Method::POST, "/api/v1/tasks", Some(task)).await;
|
||||
assert_eq!(create_response.status(), StatusCode::CREATED);
|
||||
|
||||
let run_response = send_json(
|
||||
&app.router,
|
||||
Method::POST,
|
||||
"/api/v1/tasks/bare_python/run",
|
||||
None,
|
||||
)
|
||||
.await;
|
||||
assert_eq!(run_response.status(), StatusCode::ACCEPTED);
|
||||
let run = response_json(run_response).await;
|
||||
let completed = wait_for_run(&app.router, run["id"].as_i64().unwrap()).await;
|
||||
|
||||
assert_eq!(completed["status"], "failed");
|
||||
let message = completed["error_message"]
|
||||
.as_str()
|
||||
.expect("failed run should carry an error message");
|
||||
// 运行期不再自动装依赖,只提示去编辑器里创建。
|
||||
assert!(message.contains("编辑器里用 uv 创建"), "{message}");
|
||||
app.state.shutdown();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn shell_language_runs_batch_file_with_cmd_c() {
|
||||
let app = test_app().await;
|
||||
@@ -706,11 +746,12 @@ async fn scheduler_runs_tasks_and_tracks_enable_state() {
|
||||
)
|
||||
.await;
|
||||
let runs = response_json(response).await;
|
||||
if let Some(item) = runs["items"]
|
||||
.as_array()
|
||||
.and_then(|items| items.first())
|
||||
.filter(|item| item["trigger"] == "schedule")
|
||||
{
|
||||
// 每秒触发一次,机器忙时上一轮还没结束,最新一条可能是 skipped;取真正跑起来那条。
|
||||
if let Some(item) = runs["items"].as_array().and_then(|items| {
|
||||
items
|
||||
.iter()
|
||||
.find(|item| item["trigger"] == "schedule" && item["status"] != "skipped")
|
||||
}) {
|
||||
scheduled_run = Some(item.clone());
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user