feat: 解决python虚拟环境问题
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -302,6 +302,7 @@ export interface components {
|
||||
script: string;
|
||||
/** @enum {string} */
|
||||
type: "script";
|
||||
venv?: string | null;
|
||||
working_dir: string;
|
||||
} | {
|
||||
body?: string | null;
|
||||
|
||||
@@ -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 [
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user