55 "bytes"
66 "crypto/sha1"
77 "crypto/sha256"
8+ "debug/elf"
89 "encoding/hex"
910 "fmt"
1011 "io"
@@ -23,10 +24,13 @@ import (
2324 "github.com/netlify/open-api/go/porcelain/context"
2425
2526 "github.com/go-openapi/errors"
27+ "github.com/rsc/goversion/version"
2628)
2729
2830const (
2931 preProcessingTimeout = time .Minute * 5
32+ jsRuntime = "js"
33+ goRuntime = "go"
3034)
3135
3236type uploadType int
@@ -76,8 +80,9 @@ type uploadError struct {
7680}
7781
7882type FileBundle struct {
79- Name string
80- Sum string
83+ Name string
84+ Sum string
85+ Runtime string
8186
8287 // Path OR Buffer should be populated
8388 Path string
@@ -398,7 +403,7 @@ func (n *Netlify) uploadFile(ctx context.Context, d *models.Deploy, f *FileBundl
398403 _ , operationError = n .Operations .UploadDeployFile (params , authInfo )
399404 }
400405 case functionUpload :
401- params := operations .NewUploadDeployFunctionParams ().WithDeployID (d .ID ).WithName (f .Name ).WithFileBody (f )
406+ params := operations .NewUploadDeployFunctionParams ().WithDeployID (d .ID ).WithName (f .Name ).WithFileBody (f ). WithRuntime ( & f . Runtime )
402407 if timeout != 0 {
403408 params .SetTimeout (timeout )
404409 }
@@ -500,54 +505,92 @@ func bundle(functionDir string, observer DeployObserver) (*deployFiles, error) {
500505 if err != nil {
501506 return nil , err
502507 }
508+
503509 for _ , i := range info {
504- switch filepath .Ext (i .Name ()) {
505- case ".js" :
506- file := & FileBundle {
507- Name : strings .TrimSuffix (i .Name (), filepath .Ext (i .Name ())),
508- }
510+ filePath := filepath .Join (functionDir , i .Name ())
509511
510- s := sha256 .New ()
511- buf := new (bytes.Buffer )
512- archive := zip .NewWriter (buf )
513- fileHeader , err := archive .Create (i .Name ())
512+ switch {
513+ case jsFile (i ):
514+ file , err := newFunctionFile (filePath , i , jsRuntime , observer )
514515 if err != nil {
515516 return nil , err
516517 }
517- fileEntry , err := os .Open (filepath .Join (functionDir , i .Name ()))
518+ functions .Add (file .Name , file )
519+ case goFile (filePath , i ):
520+ file , err := newFunctionFile (filePath , i , goRuntime , observer )
518521 if err != nil {
519522 return nil , err
520523 }
521- defer fileEntry .Close ()
522- if _ , err = io .Copy (fileHeader , fileEntry ); err != nil {
523- return nil , err
524- }
524+ functions .Add (file .Name , file )
525+ }
526+ }
525527
526- if err := archive .Close (); err != nil {
527- return nil , err
528- }
528+ return functions , nil
529+ }
529530
530- fileBuffer := new (bytes.Buffer )
531- m := io .MultiWriter (s , fileBuffer )
531+ func newFunctionFile (filePath string , i os.FileInfo , runtime string , observer DeployObserver ) (* FileBundle , error ) {
532+ file := & FileBundle {
533+ Name : strings .TrimSuffix (i .Name (), filepath .Ext (i .Name ())),
534+ Runtime : runtime ,
535+ }
532536
533- if _ , err := io .Copy (m , buf ); err != nil {
534- return nil , err
535- }
536- file .Sum = hex .EncodeToString (s .Sum (nil ))
537- file .Buffer = bytes .NewReader (fileBuffer .Bytes ())
538- functions .Add (file .Name , file )
537+ s := sha256 .New ()
539538
540- if observer != nil {
541- if err := observer .OnSuccessfulStep (file ); err != nil {
542- return nil , err
543- }
544- }
545- default :
546- // Ignore this file
539+ fileEntry , err := os .Open (filePath )
540+ if err != nil {
541+ return nil , err
542+ }
543+ defer fileEntry .Close ()
544+
545+ buf := new (bytes.Buffer )
546+ archive := zip .NewWriter (buf )
547+
548+ fileHeader , err := createHeader (archive , i , runtime )
549+ if err != nil {
550+ return nil , err
551+ }
552+
553+ if _ , err = io .Copy (fileHeader , fileEntry ); err != nil {
554+ return nil , err
555+ }
556+
557+ if err := archive .Close (); err != nil {
558+ return nil , err
559+ }
560+
561+ fileBuffer := new (bytes.Buffer )
562+ m := io .MultiWriter (s , fileBuffer )
563+
564+ if _ , err := io .Copy (m , buf ); err != nil {
565+ return nil , err
566+ }
567+ file .Sum = hex .EncodeToString (s .Sum (nil ))
568+ file .Buffer = bytes .NewReader (fileBuffer .Bytes ())
569+
570+ if observer != nil {
571+ if err := observer .OnSuccessfulStep (file ); err != nil {
572+ return nil , err
547573 }
548574 }
549575
550- return functions , nil
576+ return file , nil
577+ }
578+
579+ func jsFile (i os.FileInfo ) bool {
580+ return filepath .Ext (i .Name ()) == ".js"
581+ }
582+
583+ func goFile (filePath string , i os.FileInfo ) bool {
584+ if m := i .Mode (); m & 0111 == 0 { // check if it's an executable file
585+ return false
586+ }
587+
588+ if _ , err := elf .Open (filePath ); err != nil { // check if it's a linux executable
589+ return false
590+ }
591+
592+ v , err := version .ReadExe (filePath )
593+ return err == nil && strings .HasPrefix (v .Release , "go1." )
551594}
552595
553596func ignoreFile (rel string ) bool {
@@ -556,3 +599,15 @@ func ignoreFile(rel string) bool {
556599 }
557600 return false
558601}
602+
603+ func createHeader (archive * zip.Writer , i os.FileInfo , runtime string ) (io.Writer , error ) {
604+ if runtime == goRuntime {
605+ return archive .CreateHeader (& zip.FileHeader {
606+ CreatorVersion : 3 << 8 , // indicates Unix
607+ ExternalAttrs : 0777 << 16 , // -rwxrwxrwx file permissions
608+ Name : i .Name (),
609+ Method : zip .Deflate ,
610+ })
611+ }
612+ return archive .Create (i .Name ())
613+ }
0 commit comments