@@ -22,6 +22,7 @@ import {
2222 readFileSync ,
2323 symlinkSync ,
2424 chmodSync ,
25+ statSync ,
2526} from 'node:fs' ;
2627import { tmpdir } from 'node:os' ;
2728import * as path from 'node:path' ;
@@ -464,4 +465,51 @@ describe('zipBuilder.prepareDependencyArchive', () => {
464465 expect ( caught ! . message ) . to . match ( / D o c k e r f i l e \. d e p e n d e n c i e s / ) ;
465466 expect ( missing . calls . build ) . to . have . length ( 0 ) ;
466467 } ) ;
468+
469+ it ( 'stages build_native_dependencies.sh with executable permission preserved' , async function ( ) {
470+ if ( process . platform === 'win32' ) {
471+ this . skip ( ) ;
472+ return ;
473+ }
474+ chmodSync ( path . join ( baseDir , 'build_native_dependencies.sh' ) , 0o755 ) ;
475+
476+ const { runner } = makeRunner ( {
477+ imageExists : true ,
478+ onRun : ( mountPath ) => {
479+ const stagedScript = path . join ( mountPath , 'build_native_dependencies.sh' ) ;
480+ const mode = statSync ( stagedScript ) . mode & 0o777 ;
481+ expect ( mode & 0o111 , 'staged build script must have executable bits' ) . to . not . equal ( 0 ) ;
482+ writeFileSync ( path . join ( mountPath , 'native_dependencies.tar.gz' ) , 'data' ) ;
483+ } ,
484+ } ) ;
485+
486+ await prepareDependencyArchive ( baseDir , 'default' , 'script' , ( ) => { } , runner ) ;
487+ } ) ;
488+
489+ it ( 'preserves executable permissions on py-files copied via copyTree (function packages)' , async function ( ) {
490+ if ( process . platform === 'win32' ) {
491+ this . skip ( ) ;
492+ return ;
493+ }
494+
495+ const { runner } = makeRunner ( {
496+ imageExists : true ,
497+ onRun : ( mountPath ) => {
498+ const pyFilesSrc = path . join ( mountPath , 'py-files' ) ;
499+ mkdirSync ( pyFilesSrc ) ;
500+ const execScript = path . join ( pyFilesSrc , 'run_me.sh' ) ;
501+ writeFileSync ( execScript , '#!/bin/bash\necho hello\n' ) ;
502+ chmodSync ( execScript , 0o755 ) ;
503+ writeFileSync ( path . join ( pyFilesSrc , 'lib.py' ) , 'pass' ) ;
504+ } ,
505+ } ) ;
506+
507+ await prepareDependencyArchive ( baseDir , 'default' , 'function' , ( ) => { } , runner ) ;
508+
509+ const dest = path . join ( baseDir , 'payload' , 'py-files' ) ;
510+ const scriptMode = statSync ( path . join ( dest , 'run_me.sh' ) ) . mode & 0o777 ;
511+ const libMode = statSync ( path . join ( dest , 'lib.py' ) ) . mode & 0o777 ;
512+ expect ( scriptMode & 0o111 , 'executable script in py-files must retain execute bits' ) . to . not . equal ( 0 ) ;
513+ expect ( libMode & 0o111 , 'non-executable file should not gain execute bits' ) . to . equal ( 0 ) ;
514+ } ) ;
467515} ) ;
0 commit comments