feat: 左侧导航改版与 Python 镜像管理
- 应用外壳换成 stash-ui Layout:左侧导航(任务调度 / Python 镜像)+ 页面标题区,窄屏折叠为顶部导航 - 新增 python_mirrors 表与 CRUD/设默认接口,内置清华、阿里云、中科大、PyPI 官方 - Python 脚本任务运行时注入默认镜像的 PIP_INDEX_URL / UV_INDEX_URL,任务自身环境变量优先 - 新增镜像管理页与表单,支持新增、编辑、启用/停用、设默认、删除 - 重新生成 OpenAPI 与 TS 类型;补齐后端镜像测试、前端单测与 e2e,修复窄屏页面被宽表格撑开
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
collections::{BTreeMap, HashMap},
|
||||
path::{Path, PathBuf},
|
||||
process::Stdio,
|
||||
sync::{
|
||||
@@ -26,7 +26,7 @@ use tokio_util::sync::CancellationToken;
|
||||
use crate::{
|
||||
error::ApiResult,
|
||||
process_job::ProcessJob,
|
||||
python_env::prepare_script_environment,
|
||||
python_env::{is_python_interpreter, prepare_script_environment},
|
||||
runs::{
|
||||
model::{RunStatus, RunTrigger, TaskRunResponse},
|
||||
repository,
|
||||
@@ -447,11 +447,48 @@ impl TaskExecutor {
|
||||
};
|
||||
let mut command = Command::new(&prepared.interpreter);
|
||||
prepared.apply(&mut command);
|
||||
|
||||
let mut notes = Vec::new();
|
||||
if let Some(venv) = &prepared.virtual_env {
|
||||
notes.push(format!("使用 Python 虚拟环境: {}", venv.display()));
|
||||
}
|
||||
if is_python_interpreter(interpreter) {
|
||||
match crate::python_mirrors::repository::default_enabled(&self.inner.pool).await
|
||||
{
|
||||
Ok(Some(mirror)) => {
|
||||
if !env_has(env, "PIP_INDEX_URL") {
|
||||
command.env("PIP_INDEX_URL", &mirror.index_url);
|
||||
}
|
||||
if !env_has(env, "UV_INDEX_URL") {
|
||||
command.env("UV_INDEX_URL", &mirror.index_url);
|
||||
}
|
||||
if let Some(host) = mirror
|
||||
.trusted_host
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
&& !env_has(env, "PIP_TRUSTED_HOST")
|
||||
{
|
||||
command.env("PIP_TRUSTED_HOST", host);
|
||||
}
|
||||
notes.push(format!(
|
||||
"Python 包镜像: {} ({})",
|
||||
mirror.name, mirror.index_url
|
||||
));
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(error) => {
|
||||
tracing::warn!(
|
||||
task_id = %task.id,
|
||||
%error,
|
||||
"failed to load default python mirror"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
command.arg(script).args(args).current_dir(working_dir);
|
||||
let startup_note = prepared
|
||||
.virtual_env
|
||||
.as_ref()
|
||||
.map(|venv| format!("使用 Python 虚拟环境: {}", venv.display()));
|
||||
let startup_note = (!notes.is_empty()).then(|| notes.join(";"));
|
||||
self.run_command(task, run_id, cancellation, command, startup_note)
|
||||
.await
|
||||
}
|
||||
@@ -1064,6 +1101,11 @@ async fn wait_after_termination(child: &mut Child) {
|
||||
}
|
||||
}
|
||||
|
||||
fn env_has(env: &BTreeMap<String, String>, key: &str) -> bool {
|
||||
env.keys()
|
||||
.any(|candidate| candidate.eq_ignore_ascii_case(key))
|
||||
}
|
||||
|
||||
fn sensitive_values(action: &TaskAction) -> Vec<String> {
|
||||
match action {
|
||||
TaskAction::Script { env, .. } | TaskAction::Process { env, .. } => env
|
||||
|
||||
Reference in New Issue
Block a user