@@ -21,9 +21,11 @@ import (
2121 "fmt"
2222 "os"
2323 "path/filepath"
24+ "slices"
2425 "strings"
2526
2627 "github.com/charmbracelet/huh"
28+ "github.com/pkg/errors"
2729
2830 "github.com/livekit/livekit-cli/v2/pkg/util"
2931 "github.com/livekit/protocol/logger"
@@ -51,29 +53,25 @@ func CreateDockerfile(dir string, settingsMap map[string]string) error {
5153 return fmt .Errorf ("unable to fetch client settings from server, please try again later" )
5254 }
5355
54- projectType := ""
55- if isNode (dir ) {
56- projectType = "node"
57- } else if isPython (dir ) {
58- projectType = "python"
59- } else {
60- return fmt .Errorf ("unable to determine project type, please create a Dockerfile in the current directory" )
56+ projectType , err := DetectProjectType (dir )
57+ if err != nil {
58+ return errors .Wrap (err , "unable to determine project type, please create a Dockerfile in the current directory" )
6159 }
6260
6361 var dockerfileContent []byte
6462 var dockerIgnoreContent []byte
65- var err error
6663
67- dockerfileContent , err = fs .ReadFile ("examples/" + projectType + ".Dockerfile" )
64+ dockerfileContent , err = fs .ReadFile ("examples/" + string ( projectType ) + ".Dockerfile" )
6865 if err != nil {
6966 return err
7067 }
71- dockerIgnoreContent , err = fs .ReadFile ("examples/" + projectType + ".dockerignore" )
68+ dockerIgnoreContent , err = fs .ReadFile ("examples/" + string ( projectType ) + ".dockerignore" )
7269 if err != nil {
7370 return err
7471 }
7572
76- if projectType == "python" {
73+ // TODO: (@rektdeckard) support Node entrypoint validation
74+ if projectType .IsPython () {
7775 dockerfileContent , err = validateEntrypoint (dir , dockerfileContent , projectType , settingsMap )
7876 if err != nil {
7977 return err
@@ -93,52 +91,39 @@ func CreateDockerfile(dir string, settingsMap map[string]string) error {
9391 return nil
9492}
9593
96- func validateEntrypoint (dir string , dockerfileContent []byte , projectType string , settingsMap map [string ]string ) ([]byte , error ) {
97- fileList := make (map [string ]bool )
98- entries , err := os .ReadDir (dir )
99- if err != nil {
100- return nil , err
101- }
102-
103- for _ , entry := range entries {
104- if entry .IsDir () {
105- continue
106- }
107- fileList [entry .Name ()] = true
108- }
109-
94+ func validateEntrypoint (dir string , dockerfileContent []byte , projectType ProjectType , settingsMap map [string ]string ) ([]byte , error ) {
11095 valFile := func (fileName string ) (string , error ) {
111- if _ , exists := fileList [fileName ]; exists {
112- return fileName , nil
113- }
114-
115- var suffix string
116- switch projectType {
117- case "python" :
118- suffix = ".py"
119- case "node" :
120- suffix = ".js"
96+ // NOTE: we need to recurse to find entrypoints which may exist in src/ or some other directory.
97+ // This could be a lot of files, so we omit any files in .dockerignore, since they cannot be
98+ // used as entrypoints.
99+ var fileList []string
100+ if err := filepath .WalkDir (dir , func (path string , d os.DirEntry , err error ) error {
101+ if err != nil {
102+ return err
103+ }
104+ if ! d .IsDir () && strings .HasSuffix (d .Name (), ".py" ) {
105+ fileList = append (fileList , path )
106+ }
107+ return nil
108+ }); err != nil {
109+ panic (err )
121110 }
122111
123- // Collect all matching files
124- var options []string
125- for _ , entry := range entries {
126- if strings .HasSuffix (entry .Name (), suffix ) {
127- options = append (options , entry .Name ())
128- }
112+ if slices .Contains (fileList , fileName ) {
113+ return fileName , nil
129114 }
130115
131116 // If no matching files found, return early
132- if len (options ) == 0 {
117+ if len (fileList ) == 0 {
133118 return "" , nil
134119 }
135120
136121 var selected string
137122 form := huh .NewForm (
138123 huh .NewGroup (
139124 huh .NewSelect [string ]().
140- Title (fmt .Sprintf ("Select %s file to use as entrypoint" , projectType )).
141- Options (huh .NewOptions (options ... )... ).
125+ Title (fmt .Sprintf ("Select %s file to use as entrypoint" , projectType . Lang () )).
126+ Options (huh .NewOptions (fileList ... )... ).
142127 Value (& selected ).
143128 WithTheme (util .Theme ),
144129 ),
@@ -152,8 +137,7 @@ func validateEntrypoint(dir string, dockerfileContent []byte, projectType string
152137 return selected , nil
153138 }
154139
155- err = validateSettingsMap (settingsMap , []string {"python_entrypoint" })
156- if err != nil {
140+ if err := validateSettingsMap (settingsMap , []string {"python_entrypoint" }); err != nil {
157141 return nil , err
158142 }
159143
@@ -165,7 +149,7 @@ func validateEntrypoint(dir string, dockerfileContent []byte, projectType string
165149
166150 lines := bytes .Split (dockerfileContent , []byte ("\n " ))
167151 var result bytes.Buffer
168- for i := 0 ; i < len ( lines ); i ++ {
152+ for i := range lines {
169153 line := lines [i ]
170154 trimmedLine := bytes .TrimSpace (line )
171155
@@ -226,7 +210,7 @@ func validateEntrypoint(dir string, dockerfileContent []byte, projectType string
226210 return nil , err
227211 }
228212 for i , arg := range cmdArray {
229- if strings .HasSuffix (arg , ".py" ) {
213+ if strings .HasSuffix (arg , projectType . FileExt () ) {
230214 cmdArray [i ] = newEntrypoint
231215 break
232216 }
@@ -237,7 +221,7 @@ func validateEntrypoint(dir string, dockerfileContent []byte, projectType string
237221 }
238222 fmt .Fprintf (& result , "CMD %s\n " , newJSON )
239223 }
240- } else if bytes .HasPrefix (trimmedLine , [] byte ( fmt .Sprintf ( "RUN python %s" , pythonEntrypoint ) )) {
224+ } else if bytes .HasPrefix (trimmedLine , fmt .Appendf ( nil , "RUN python %s" , pythonEntrypoint )) {
241225 line = bytes .ReplaceAll (line , []byte (pythonEntrypoint ), []byte (newEntrypoint ))
242226 result .Write (line )
243227 if i < len (lines )- 1 {
0 commit comments