1
1
import fs from "node:fs" ;
2
2
import assert from "node:assert" ;
3
- import { execa } from "execa" ;
4
3
import { additionalFiles } from "./core/additionalFiles.js" ;
5
4
import { BuildManifest } from "@trigger.dev/core/v3" ;
6
5
import { BuildContext , BuildExtension } from "@trigger.dev/core/v3/build" ;
7
6
import { logger } from "@trigger.dev/sdk/v3" ;
8
-
9
- import type { VerboseObject } from "execa" ;
7
+ import { x , Options as XOptions , Result } from "tinyexec" ;
10
8
11
9
export type PythonOptions = {
12
10
requirements ?: string [ ] ;
@@ -30,8 +28,6 @@ export type PythonOptions = {
30
28
scripts ?: string [ ] ;
31
29
} ;
32
30
33
- type ExecaOptions = Parameters < typeof execa > [ 1 ] ;
34
-
35
31
const splitAndCleanComments = ( str : string ) =>
36
32
str
37
33
. split ( "\n" )
@@ -124,26 +120,24 @@ class PythonExtension implements BuildExtension {
124
120
125
121
export const run = async (
126
122
scriptArgs : string [ ] = [ ] ,
127
- options : ExecaOptions = { }
128
- ) : Promise < ReturnType < typeof execa > > => {
123
+ options : Partial < XOptions > = { }
124
+ ) : Promise < Result > => {
129
125
const pythonBin = process . env . PYTHON_BIN_PATH || "python" ;
130
126
131
- const result = await execa ( {
132
- shell : true ,
133
- verbose : ( line : string , obj : VerboseObject ) => logger . debug ( obj . message , obj ) ,
127
+ const result = await x ( pythonBin , scriptArgs , {
134
128
...options ,
135
- } ) ( pythonBin , scriptArgs ) ;
129
+ throwOnError : false , // Ensure errors are handled manually
130
+ } ) ;
136
131
137
132
try {
138
- assert ( ! result . failed , `Python command failed: ${ result . stderr } \nCommand: ${ result . command } ` ) ;
139
133
assert (
140
134
result . exitCode === 0 ,
141
135
`Python command exited with non-zero code ${ result . exitCode } \nStdout: ${ result . stdout } \nStderr: ${ result . stderr } `
142
136
) ;
143
137
} catch ( error ) {
144
138
logger . error ( "Python command execution failed" , {
145
139
error : error instanceof Error ? error . message : error ,
146
- command : result . command ,
140
+ command : ` ${ pythonBin } ${ scriptArgs . join ( " " ) } ` ,
147
141
stdout : result . stdout ,
148
142
stderr : result . stderr ,
149
143
exitCode : result . exitCode ,
@@ -157,25 +151,23 @@ export const run = async (
157
151
export const runScript = (
158
152
scriptPath : string ,
159
153
scriptArgs : string [ ] = [ ] ,
160
- options : ExecaOptions = { }
154
+ options : Partial < XOptions > = { }
161
155
) => {
162
156
assert ( scriptPath , "Script path is required" ) ;
163
157
assert ( fs . existsSync ( scriptPath ) , `Script does not exist: ${ scriptPath } ` ) ;
164
158
165
159
return run ( [ scriptPath , ...scriptArgs ] , options ) ;
166
160
} ;
167
161
168
- export const runInline = async ( scriptContent : string , options : ExecaOptions = { } ) => {
162
+ export const runInline = async ( scriptContent : string , options : Partial < XOptions > = { } ) => {
169
163
assert ( scriptContent , "Script content is required" ) ;
170
164
171
- // Create a temporary file with restricted permissions
172
165
const tmpFile = `/tmp/script_${ Date . now ( ) } .py` ;
173
166
await fs . promises . writeFile ( tmpFile , scriptContent , { mode : 0o600 } ) ;
174
167
175
168
try {
176
169
return await runScript ( tmpFile , [ ] , options ) ;
177
170
} finally {
178
- // Clean up temporary file
179
171
await fs . promises . unlink ( tmpFile ) ;
180
172
}
181
173
} ;
0 commit comments