@@ -47,6 +47,38 @@ function generateEphemeralDenoConfig(targetPath: string, denoConfigPath: string,
4747 fs . writeFileSync ( targetPath , JSON . stringify ( runtimeConfig , null , '\t' ) ) ;
4848}
4949
50+ /**
51+ * Ensures a directory symlink exists at `symlinkPath` pointing to `targetPath`.
52+ *
53+ * First removes whatever currently lives at `symlinkPath` (broken symlink,
54+ * wrong target, or a non-symlink entry), ignoring ENOENT if nothing is there.
55+ * Then creates the symlink, catching EEXIST to guard against race conditions.
56+ */
57+ export function ensureSymlink ( symlinkPath : string , targetPath : string ) : void {
58+ try {
59+ const currentTarget = fs . readlinkSync ( symlinkPath ) ;
60+
61+ if ( currentTarget === targetPath ) {
62+ return ;
63+ }
64+
65+ fs . rmSync ( symlinkPath , { recursive : true , force : true } ) ;
66+ } catch ( err : unknown ) {
67+ if ( ( err as NodeJS . ErrnoException ) . code !== 'ENOENT' ) {
68+ fs . rmSync ( symlinkPath , { recursive : true , force : true } ) ;
69+ }
70+ }
71+
72+ // Create the symlink; guard against a concurrent creator
73+ try {
74+ fs . symlinkSync ( targetPath , symlinkPath , 'dir' ) ;
75+ } catch ( err : unknown ) {
76+ if ( ( err as NodeJS . ErrnoException ) . code !== 'EEXIST' ) {
77+ throw err ;
78+ }
79+ }
80+ }
81+
5082export class DenoRuntimeSubprocessController extends BaseRuntimeSubprocessController {
5183 private readonly denoBin = 'deno' ;
5284
@@ -75,13 +107,7 @@ export class DenoRuntimeSubprocessController extends BaseRuntimeSubprocessContro
75107 * Deno 2.x refuses to run scripts inside the node_modules, so we create a symlink to the deno runtime files in the temp directory
76108 * The temp directory is the same we are given by the host to store temporary upload files
77109 */
78- try {
79- fs . symlinkSync ( path . dirname ( this . denoConfigPath ) , path . dirname ( this . denoRuntimePath ) , 'dir' ) ;
80- } catch ( reason : unknown ) {
81- if ( ( reason as NodeJS . ErrnoException ) . code !== 'EEXIST' ) {
82- throw reason ;
83- }
84- }
110+ ensureSymlink ( path . dirname ( this . denoRuntimePath ) , path . dirname ( this . denoConfigPath ) ) ;
85111
86112 // Generate a runtime config with the resolved absolute path for @rocket .chat/apps-engine/ and @rocket.chat/apps/ paths
87113 generateEphemeralDenoConfig ( this . denoEphemeralConfigPath , this . denoConfigPath , this . appsEnginePath , this . packagePath ) ;
0 commit comments