@@ -49,45 +49,88 @@ func HasDockerfile(dir string) (bool, error) {
4949 return false , nil
5050}
5151
52- func CreateDockerfile (dir string , projectType ProjectType , settingsMap map [string ]string ) error {
52+ func CreateDockerfile (dir string , settingsMap map [string ]string ) error {
5353 if len (settingsMap ) == 0 {
5454 return fmt .Errorf ("unable to fetch client settings from server, please try again later" )
5555 }
5656
57+ projectType , err := DetectProjectType (dir )
58+ if err != nil {
59+ return fmt .Errorf (`× Unable to determine project type
60+
61+ Supported project types:
62+ • Python: requires requirements.txt or pyproject.toml
63+ • Node.js: requires package.json
64+
65+ Please ensure your project has the appropriate dependency file, or create a Dockerfile manually in the current directory` )
66+ }
67+
68+ // Provide user feedback about detected project type
69+ switch projectType {
70+ case ProjectTypeNode :
71+ fmt .Printf ("✔ Detected Node.js project (found %s)\n " , util .Accented ("package.json" ))
72+ fmt .Printf (" Using template [%s] with npm/yarn support\n " , util .Accented ("node" ))
73+ case ProjectTypePythonUV :
74+ fmt .Printf ("✔ Detected Python project with UV package manager\n " )
75+ fmt .Printf (" Using template [%s] for faster builds\n " , util .Accented ("python.uv" ))
76+ // Validate UV project setup
77+ validateUVProject (dir )
78+ case ProjectTypePythonPip :
79+ fmt .Printf ("✔ Detected Python project with pip package manager\n " )
80+ fmt .Printf (" Using template [%s]\n " , util .Accented ("python.pip" ))
81+ }
82+
5783 var dockerfileContent []byte
5884 var dockerIgnoreContent []byte
59- var err error
6085
6186 dockerfileContent , err = fs .ReadFile ("examples/" + string (projectType ) + ".Dockerfile" )
6287 if err != nil {
63- return err
88+ return fmt . Errorf ( "failed to load Dockerfile template '%s': %w" , string ( projectType ), err )
6489 }
90+
6591 dockerIgnoreContent , err = fs .ReadFile ("examples/" + string (projectType ) + ".dockerignore" )
6692 if err != nil {
67- return err
93+ return fmt . Errorf ( "failed to load .dockerignore template for '%s': %w" , string ( projectType ), err )
6894 }
6995
7096 // TODO: (@rektdeckard) support Node entrypoint validation
7197 if projectType .IsPython () {
7298 dockerfileContent , err = validateEntrypoint (dir , dockerfileContent , dockerIgnoreContent , projectType , settingsMap )
7399 if err != nil {
74- return err
100+ return fmt . Errorf ( "failed to validate Python entry point: %w" , err )
75101 }
76102 }
77103
78104 err = os .WriteFile (filepath .Join (dir , "Dockerfile" ), dockerfileContent , 0644 )
79105 if err != nil {
80- return err
106+ return fmt . Errorf ( "failed to write Dockerfile: %w" , err )
81107 }
82108
83109 err = os .WriteFile (filepath .Join (dir , ".dockerignore" ), dockerIgnoreContent , 0644 )
84110 if err != nil {
85- return err
111+ return fmt . Errorf ( "failed to write .dockerignore: %w" , err )
86112 }
87113
114+ fmt .Printf ("\n ✔ Successfully generated Docker files:\n " )
115+ fmt .Printf (" %s - Container build instructions\n " , util .Accented ("Dockerfile" ))
116+ fmt .Printf (" %s - Files excluded from build context\n " , util .Accented (".dockerignore" ))
117+ fmt .Printf ("\n Next steps:\n " )
118+ fmt .Printf (" ► Review the %s and uncomment any needed system packages\n " , util .Accented ("Dockerfile" ))
119+ fmt .Printf (" ► Build your agent: docker build -t my-agent .\n " )
120+ fmt .Printf (" ► Test locally: docker run my-agent\n " )
121+
88122 return nil
89123}
90124
125+ func validateUVProject (dir string ) {
126+ uvLockPath := filepath .Join (dir , "uv.lock" )
127+ if _ , err := os .Stat (uvLockPath ); err != nil {
128+ fmt .Printf ("! Warning: UV project detected but %s file not found\n " , util .Accented ("uv.lock" ))
129+ fmt .Printf (" Consider running %s to generate %s for reproducible builds\n " , util .Accented ("uv lock" ), util .Accented ("uv.lock" ))
130+ fmt .Printf (" This ensures consistent dependency versions across environments\n \n " )
131+ }
132+ }
133+
91134func validateEntrypoint (dir string , dockerfileContent []byte , dockerignoreContent []byte , projectType ProjectType , settingsMap map [string ]string ) ([]byte , error ) {
92135 valFile := func (fileName string ) (string , error ) {
93136 // Parse dockerignore patterns to filter out files that won't be in build context
0 commit comments