feat: 解决python虚拟环境问题

This commit is contained in:
2026-10-19 23:32:08 +08:00
parent 00a608c149
commit 23565b40a4
15 changed files with 611 additions and 10 deletions
+7 -1
View File
@@ -67,11 +67,17 @@ test('manages a task and reads a real run log', async ({ page, request }) => {
await newTaskDialog
.getByPlaceholder('参数值 (可选,无值可留空)')
.fill('full')
await newTaskDialog
.getByLabel(/虚拟环境目录/)
.fill('.venv')
await newTaskDialog
.getByText('调用预览 (Runtime Preview)')
.scrollIntoViewIfNeeded()
await expect(
newTaskDialog.getByText('--mode full', { exact: false }),
newTaskDialog.getByText(
'.venv\\Scripts\\python.exe main.py --mode full',
{ exact: false },
),
).toBeVisible()
await page.screenshot({
path: 'test-results/dashboard-task-editor-runtime.png',
+1
View File
@@ -302,6 +302,7 @@ export interface components {
script: string;
/** @enum {string} */
type: "script";
venv?: string | null;
working_dir: string;
} | {
body?: string | null;
+25 -1
View File
@@ -328,6 +328,16 @@ export function TaskEditorModal({
/>
</Field>
</div>
<Field
label="虚拟环境目录 (可选)"
hint="留空时自动从工作目录及上级目录探测 .venv、venv、env"
>
<input
{...register('script.venv')}
className="field-input mono"
placeholder="例如:.venv"
/>
</Field>
<Field
label="脚本文件或相对路径"
error={errors.script?.script?.message}
@@ -402,6 +412,11 @@ export function TaskEditorModal({
<span>Windows 原生环境</span>
</div>
<div className="cli-preview-body">
{scriptPreview.venv.trim() ? (
<div className="cli-line-env">
set VIRTUAL_ENV={scriptPreview.venv.trim()}
</div>
) : null}
{scriptPreview.env
.filter((item) => item.key.trim())
.map((item, index) => (
@@ -416,7 +431,7 @@ export function TaskEditorModal({
</div>
))}
<div className="cli-line-cmd">
{scriptPreview.interpreter.trim() || 'python.exe'}{' '}
{formatInterpreterPreview(scriptPreview)}{' '}
{scriptPreview.script.trim() || 'main.py'}
{formatArgumentPreview(scriptPreview.args)}
</div>
@@ -598,6 +613,15 @@ function formatArgumentPreview(argumentsList: KeyValueFormValue[]): string {
return values.length ? ` ${values.join(' ')}` : ''
}
function formatInterpreterPreview(
script: TaskFormValues['script'],
): string {
if (script.venv.trim()) {
return `${script.venv.trim().replace(/[\\/]+$/, '')}\\Scripts\\python.exe`
}
return script.interpreter.trim() || 'python.exe'
}
function isSensitiveName(name: string): boolean {
const normalized = name.toUpperCase()
return [
+22
View File
@@ -6,6 +6,28 @@ import {
} from './taskForm'
describe('task form payload', () => {
it('submits an explicit Python virtual environment', () => {
const values = createEmptyTaskFormValues()
values.id = 'venv_task'
values.name = 'Venv task'
values.script.working_dir = 'D:\\Projects\\venv-task'
values.script.venv = '.venv'
values.script.interpreter = 'python.exe'
values.script.script = 'main.py'
values.script.args = [{ key: '--mode', value: 'full' }]
const payload = valuesToCreateRequest(values)
expect(payload.action).toMatchObject({
type: 'script',
working_dir: 'D:\\Projects\\venv-task',
venv: '.venv',
interpreter: 'python.exe',
script: 'main.py',
args: ['--mode', 'full'],
})
})
it('creates an HTTP action instead of forcing script type', () => {
const values = createEmptyTaskFormValues()
values.id = 'health_check'
+5
View File
@@ -22,6 +22,7 @@ export interface TaskFormValues {
actionType: TaskActionType
script: {
working_dir: string
venv: string
interpreter: string
script: string
args: KeyValueFormValue[]
@@ -86,6 +87,7 @@ export const taskFormSchema = z
actionType: z.enum(['script', 'http', 'process']),
script: z.object({
working_dir: z.string(),
venv: z.string(),
interpreter: z.string(),
script: z.string(),
args: z.array(keyValueSchema),
@@ -168,6 +170,7 @@ export function createEmptyTaskFormValues(): TaskFormValues {
actionType: 'script',
script: {
working_dir: '',
venv: '',
interpreter: 'python.exe',
script: 'main.py',
args: [],
@@ -202,6 +205,7 @@ export function taskToFormValues(task: Task): TaskFormValues {
if (task.action.type === 'script') {
values.script = {
working_dir: task.action.working_dir,
venv: task.action.venv ?? '',
interpreter: task.action.interpreter,
script: task.action.script,
args: argsToRows(task.action.args ?? []),
@@ -240,6 +244,7 @@ export function valuesToAction(values: TaskFormValues): TaskAction {
return {
type: 'script',
working_dir: values.script.working_dir.trim(),
venv: values.script.venv.trim() || null,
interpreter: values.script.interpreter.trim(),
script: values.script.script.trim(),
args: argumentsToArray(values.script.args),