truefoundry 0.2.0rc8__tar.gz → 0.2.0rc10__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of truefoundry might be problematic. Click here for more details.

Files changed (37) hide show
  1. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/PKG-INFO +4 -4
  2. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/pyproject.toml +3 -4
  3. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/cli.py +63 -41
  4. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/constants.py +6 -0
  5. truefoundry-0.2.0rc10/truefoundry/autodeploy/exception.py +4 -0
  6. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/tools/commit.py +6 -2
  7. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/tools/docker_run.py +5 -7
  8. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/tools/read_file.py +13 -5
  9. truefoundry-0.2.0rc10/truefoundry/autodeploy/utils/client.py +12 -0
  10. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/deploy/cli/cli.py +4 -0
  11. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/deploy/cli/deploy.py +45 -57
  12. truefoundry-0.2.0rc8/truefoundry/autodeploy/exception.py +0 -2
  13. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/README.md +0 -0
  14. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/__init__.py +0 -0
  15. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/__init__.py +0 -0
  16. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/agents/__init__.py +0 -0
  17. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/agents/base.py +0 -0
  18. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/agents/developer.py +0 -0
  19. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/agents/project_identifier.py +0 -0
  20. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/agents/tester.py +0 -0
  21. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/logger.py +0 -0
  22. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/tools/__init__.py +0 -0
  23. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/tools/ask.py +0 -0
  24. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/tools/base.py +0 -0
  25. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/tools/docker_build.py +0 -0
  26. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/tools/file_type_counts.py +0 -0
  27. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/tools/list_files.py +0 -0
  28. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/tools/send_request.py +0 -0
  29. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/tools/write_file.py +0 -0
  30. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/utils/diff.py +0 -0
  31. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/autodeploy/utils/pydantic_compat.py +0 -0
  32. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/cli/__init__.py +0 -0
  33. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/cli/__main__.py +0 -0
  34. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/deploy/__init__.py +0 -0
  35. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/deploy/cli/__init__.py +0 -0
  36. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/langchain/__init__.py +0 -0
  37. {truefoundry-0.2.0rc8 → truefoundry-0.2.0rc10}/truefoundry/ml/__init__.py +0 -0
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: truefoundry
3
- Version: 0.2.0rc8
3
+ Version: 0.2.0rc10
4
4
  Summary: Truefoundry CLI
5
5
  Author: Abhishek Choudhary
6
6
  Author-email: abhichoudhary06@gmail.com
7
- Requires-Python: >=3.8.1,<3.13
7
+ Requires-Python: >=3.8,<3.13
8
8
  Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.8
9
10
  Classifier: Programming Language :: Python :: 3.9
10
11
  Classifier: Programming Language :: Python :: 3.10
11
12
  Classifier: Programming Language :: Python :: 3.11
@@ -14,14 +15,13 @@ Provides-Extra: ml
14
15
  Requires-Dist: docker (>=7.0.0,<8.0.0)
15
16
  Requires-Dist: gitignorefile (>=1.1.2,<2.0.0)
16
17
  Requires-Dist: gitpython (>=3.1.43,<4.0.0)
17
- Requires-Dist: inquirer (>=3.2.4,<4.0.0)
18
18
  Requires-Dist: mlfoundry (==0.10.9) ; extra == "ml"
19
19
  Requires-Dist: openai (>=1.16.2,<2.0.0)
20
20
  Requires-Dist: pydantic (>=1.10.0,<3)
21
21
  Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
22
22
  Requires-Dist: requests (>=2.31.0,<3.0.0)
23
23
  Requires-Dist: rich (>=13.7.1,<14.0.0)
24
- Requires-Dist: servicefoundry (==0.10.10)
24
+ Requires-Dist: servicefoundry (==0.10.11)
25
25
  Description-Content-Type: text/markdown
26
26
 
27
27
  # Truefoundry
@@ -1,13 +1,13 @@
1
1
  [tool.poetry]
2
2
  name = "truefoundry"
3
- version = "0.2.0rc8"
3
+ version = "0.2.0rc10"
4
4
  description = "Truefoundry CLI"
5
5
  authors = ["Abhishek Choudhary <abhichoudhary06@gmail.com>"]
6
6
  readme = "README.md"
7
7
 
8
8
  [tool.poetry.dependencies]
9
- python = "^3.8.1,<3.13"
10
- servicefoundry = "0.10.10"
9
+ python = "^3.8,<3.13"
10
+ servicefoundry = "0.10.11"
11
11
  mlfoundry = { version = "0.10.9", optional = true }
12
12
  openai = "^1.16.2"
13
13
  docker = "^7.0.0"
@@ -17,7 +17,6 @@ requests = "^2.31.0"
17
17
  python-dotenv = "^1.0.1"
18
18
  gitignorefile = "^1.1.2"
19
19
  gitpython = "^3.1.43"
20
- inquirer = "^3.2.4"
21
20
 
22
21
  [tool.poetry.extras]
23
22
  ml = ["mlfoundry"]
@@ -1,4 +1,3 @@
1
- import logging
2
1
  import os
3
2
  import re
4
3
  import sys
@@ -6,19 +5,10 @@ from typing import Dict, Optional
6
5
 
7
6
  import click
8
7
  import docker
9
- import inquirer
10
- from dotenv import dotenv_values
11
-
12
- from truefoundry.autodeploy.exception import GitBinaryNotFoundException
13
-
14
- try:
15
- from git import GitCommandError, Repo
16
- from git.exc import InvalidGitRepositoryError
17
- except ImportError as ex:
18
- raise GitBinaryNotFoundException from ex
19
-
8
+ import questionary
20
9
  import requests
21
- from click.exceptions import ClickException
10
+ import yaml
11
+ from dotenv import dotenv_values
22
12
  from openai import OpenAI
23
13
  from rich.console import Console
24
14
  from rich.prompt import Prompt
@@ -26,6 +16,9 @@ from rich.status import Status
26
16
  from servicefoundry import Build, DockerFileBuild, Job, LocalSource, Port, Service
27
17
  from servicefoundry.cli.const import COMMAND_CLS
28
18
  from servicefoundry.lib.auth.servicefoundry_session import ServiceFoundrySession
19
+ from servicefoundry.lib.clients.service_foundry_client import (
20
+ ServiceFoundryServiceClient,
21
+ )
29
22
 
30
23
  from truefoundry.autodeploy.agents.developer import Developer
31
24
  from truefoundry.autodeploy.agents.project_identifier import (
@@ -40,9 +33,13 @@ from truefoundry.autodeploy.constants import (
40
33
  AUTODEPLOY_OPENAI_BASE_URL,
41
34
  AUTODEPLOY_TFY_BASE_URL,
42
35
  )
36
+ from truefoundry.autodeploy.exception import InvalidRequirementsException
43
37
  from truefoundry.autodeploy.tools.ask import AskQuestion
44
38
  from truefoundry.autodeploy.tools.commit import CommitConfirmation
45
39
  from truefoundry.autodeploy.tools.docker_run import DockerRun, DockerRunLog
40
+ from truefoundry.autodeploy.utils.client import get_git_binary
41
+
42
+ git = get_git_binary()
46
43
 
47
44
 
48
45
  def _get_openai_client() -> OpenAI:
@@ -62,11 +59,38 @@ def _get_openai_client() -> OpenAI:
62
59
  resp = resp.json()
63
60
  return OpenAI(api_key=resp["jwtToken"], base_url=resp["inferenceBaseURL"])
64
61
  except requests.exceptions.HTTPError as http_error:
65
- raise ClickException(
66
- f"An error occurred while connecting to the Truefoundry server.\nThe server responded with status code {http_error.response.status_code}."
67
- ) from http_error
62
+ if http_error.response.status_code in [401, 403]:
63
+ raise InvalidRequirementsException(
64
+ message='Unauthorized access to Truefoundry server. Please verify your credentials and ensure you have the necessary access rights.\nIf you wish to proceed without Truefoundry AI, you need to either have a truefoundry.yaml file in your project root or pass the path to a yaml file using the "--file file_name" option.'
65
+ ) from http_error
66
+
67
+ raise http_error
68
+
68
69
  except Exception as e:
69
- raise ClickException(message=str(e)) from e
70
+ raise InvalidRequirementsException(message=str(e)) from e
71
+
72
+
73
+ def _get_service_port(name: str, workspace_fqn: str, port: int) -> Port:
74
+ client = ServiceFoundryServiceClient()
75
+ workspaces = client.get_workspace_by_fqn(workspace_fqn=workspace_fqn)
76
+ if len(workspaces) == 0:
77
+ raise click.UsageError("Invalid workspace")
78
+ workspace = workspaces[0]
79
+ cluster = client.get_cluster(cluster_id=workspace.clusterId)
80
+
81
+ generated_name = name + "-" + workspace.name
82
+ base_domain_urls = cluster["metadata"].get("baseDomainURLs")
83
+ if not base_domain_urls:
84
+ return Port(port=port, expose=False)
85
+
86
+ base_domain_url = base_domain_urls[0]
87
+
88
+ if "*" in base_domain_url:
89
+ return Port(
90
+ port=port, host=base_domain_url.replace("*", generated_name).strip("/")
91
+ )
92
+
93
+ return Port(port=port, host=base_domain_url, path=f"/{generated_name}/")
70
94
 
71
95
 
72
96
  def deploy_component(
@@ -79,8 +103,6 @@ def deploy_component(
79
103
  command: Optional[str] = None,
80
104
  port: Optional[int] = None,
81
105
  ):
82
- logging.basicConfig(level=logging.INFO)
83
-
84
106
  if not os.path.exists(os.path.join(project_root_path, dockerfile_path)):
85
107
  raise FileNotFoundError("Dockerfile not found in the project.")
86
108
 
@@ -94,14 +116,20 @@ def deploy_component(
94
116
  if component_type == ComponentType.SERVICE:
95
117
  if port is None:
96
118
  raise ValueError("Port is required for deploying service")
119
+ service_port = _get_service_port(
120
+ name=name, workspace_fqn=workspace_fqn, port=port
121
+ )
122
+
97
123
  app = Service(
98
124
  name=name,
99
125
  image=image,
100
- ports=[Port(port=port, expose=False)],
126
+ ports=[service_port],
101
127
  env=env,
102
128
  )
103
129
  else:
104
130
  app = Job(name=name, image=image, env=env)
131
+ with open("truefoundry.yaml", "w") as application_spec:
132
+ yaml.dump(app.dict(), application_spec, indent=2)
105
133
  app.deploy(workspace_fqn=workspace_fqn)
106
134
 
107
135
 
@@ -117,7 +145,7 @@ def _parse_env(project_root_path: str, env_path: str) -> Dict:
117
145
 
118
146
  def _check_repo(project_root_path: str, console: Console):
119
147
  try:
120
- repo = Repo(project_root_path)
148
+ repo = git.Repo(path=project_root_path, search_parent_directories=True)
121
149
  if repo.is_dirty():
122
150
  console.print(
123
151
  "[bold red]Error:[/] The repository has uncommitted changes. Please commit or stash them before proceeding."
@@ -142,14 +170,14 @@ def _check_repo(project_root_path: str, console: Console):
142
170
  f"[bold magenta]TrueFoundry:[/] Continuing on [green]{current_active_branch!r}[/]"
143
171
  )
144
172
 
145
- except InvalidGitRepositoryError:
173
+ except git.exc.InvalidGitRepositoryError:
146
174
  console.print(
147
175
  "[red]Error:[/] This operation can only be performed inside a Git repository.\n"
148
176
  "Execute 'git init' to create a new repository."
149
177
  )
150
178
  sys.exit(1)
151
179
 
152
- except GitCommandError as gce:
180
+ except git.GitCommandError as gce:
153
181
  console.print(
154
182
  f"Command execution failed due to the following error:[red]{gce.stderr}[/]".replace(
155
183
  "\n stderr:", ""
@@ -184,7 +212,7 @@ def _update_status(event, status: Status):
184
212
 
185
213
  if isinstance(event, DockerRunLog):
186
214
  status.update(
187
- "[bold cyan]Running:[/] [bold magenta]TrueFoundry[/] is executing the Docker container. Press [yellow]control-c[/] to stop waiting for additional logs..."
215
+ "[bold cyan]Running:[/] [bold magenta]TrueFoundry[/] is executing the Docker container. Press [yellow]control + c[/] to stop waiting for additional logs..."
188
216
  )
189
217
 
190
218
 
@@ -199,12 +227,10 @@ def _get_default_project_name(project_root_path: str):
199
227
  def _get_docker(console: Console) -> docker.DockerClient:
200
228
  try:
201
229
  return docker.from_env()
202
- except Exception:
203
- console.print_exception(extra_lines=0, max_frames=1)
204
- console.print(
205
- "[bold red]Error:[/] Could not connect to Docker, please check whether the Docker daemon is running."
206
- )
207
- sys.exit(1)
230
+ except Exception as e:
231
+ raise InvalidRequirementsException(
232
+ message="Could not connect to Docker, please check whether the Docker daemon is running."
233
+ ) from e
208
234
 
209
235
 
210
236
  def cli(project_root_path: str, deploy: bool, workspace_fqn: str = None):
@@ -239,21 +265,17 @@ def cli(project_root_path: str, deploy: bool, workspace_fqn: str = None):
239
265
  "Service: An application that runs continuously. Example: web servers, workers polling a job queue, etc.": "SERVICE",
240
266
  "Job: An application that runs once and then stops. Example: Training an ML model, running a script, etc.": "JOB",
241
267
  }
242
- component = inquirer.prompt(
243
- [
244
- inquirer.List(
245
- "component",
246
- message="TrueFoundry: Is your project a",
247
- choices=choices.keys(),
248
- )
249
- ]
250
- )["component"]
268
+ component = questionary.select(
269
+ "TrueFoundry: Is your project a", choices=choices.keys()
270
+ ).ask()
251
271
  component_type = ComponentType[choices[component]]
252
272
  while True:
253
273
  name = Prompt.ask(
254
- "[bold magenta]TrueFoundry:[/] Name of deployment",
274
+ "[bold magenta]TrueFoundry:[/] Name of deployment, or press [green]Enter[/] to select default. "
275
+ f"default: [green]{_get_default_project_name(project_root_path)}",
255
276
  console=console,
256
277
  default=_get_default_project_name(project_root_path),
278
+ show_default=False,
257
279
  )
258
280
  if not re.match(r"^[a-z][a-z0-9\-]{1,30}[a-z0-9]$", name):
259
281
  console.print(
@@ -263,7 +285,7 @@ def cli(project_root_path: str, deploy: bool, workspace_fqn: str = None):
263
285
  else:
264
286
  break
265
287
  command = Prompt.ask(
266
- "[bold magenta]TrueFoundry:[/] Command to run the application",
288
+ "[bold magenta]TrueFoundry:[/] Command to run the application or press [green]Enter[/] to skip",
267
289
  console=console,
268
290
  show_default=False,
269
291
  default=None,
@@ -20,3 +20,9 @@ ABOUT_AUTODEPLOY = """To deploy your project, we will generate the deployment co
20
20
  We will analyze your codebase using our AI agent and make the required changes so that we can build and deploy the code.
21
21
  We will confirm all the changes with you.
22
22
  """
23
+ # The maximum file size to read is set to 10KB.
24
+ # This limit is determined by the token limit of the LLM used, which is 128,000 tokens.
25
+ # Given that one token is approximately equivalent to 4 English characters,
26
+ # a 10KB file size limit (or ~10,000 characters | ~2500 tokens) ensures that the file content,
27
+ # along with any additional context and instructions for the LLM, fits within the model's token limit.
28
+ MAX_FILE_SIZE_READ = 10 * 1024
@@ -0,0 +1,4 @@
1
+ class InvalidRequirementsException(Exception):
2
+ def __init__(self, message: str) -> None:
3
+ super().__init__(message)
4
+ self.message = message
@@ -4,7 +4,6 @@ import os
4
4
  import tempfile
5
5
  from typing import Any, Generator, Optional
6
6
 
7
- from git import Repo
8
7
  from pydantic import Field
9
8
  from rich.console import Console
10
9
  from rich.padding import Padding
@@ -18,9 +17,12 @@ from truefoundry.autodeploy.tools.base import (
18
17
  ResponseEvent,
19
18
  Tool,
20
19
  )
20
+ from truefoundry.autodeploy.utils.client import get_git_binary
21
21
  from truefoundry.autodeploy.utils.diff import LLMDiff
22
22
  from truefoundry.autodeploy.utils.pydantic_compat import model_dump
23
23
 
24
+ git = get_git_binary()
25
+
24
26
 
25
27
  class CommitConfirmation(Event):
26
28
  patch: str
@@ -109,7 +111,9 @@ The patch should have any changes that is not described in the commit message.
109
111
 
110
112
  def __init__(self, project_root_path: str):
111
113
  self.project_root_path = project_root_path
112
- self.repo = Repo(path=self.project_root_path, search_parent_directories=False)
114
+ self.repo = git.Repo(
115
+ path=self.project_root_path, search_parent_directories=True
116
+ )
113
117
 
114
118
  def run(
115
119
  self,
@@ -20,14 +20,10 @@ from truefoundry.autodeploy.tools.base import (
20
20
 
21
21
 
22
22
  class DockerRunLog(Event):
23
- index: int
24
23
  log: str
25
24
 
26
25
  def render(self, console: Console):
27
- # if self.index > 1:
28
- # print("\r", end="")
29
26
  console.print(Padding.indent(renderable=Text.from_ansi(self.log), level=2))
30
- # console.print("Press control-c to stop waiting for more logs", end="")
31
27
 
32
28
 
33
29
  class DockerRun(Tool):
@@ -128,13 +124,15 @@ This will not be passed if the container is still running.
128
124
  exit_code = None
129
125
 
130
126
  all_logs = []
131
- logs = container.logs(stream=True)
132
127
 
133
128
  try:
134
- for i, log in enumerate(logs):
129
+ # This acts as a signal to update status.
130
+ yield DockerRunLog(log="")
131
+ logs = container.logs(stream=True)
132
+ for log in logs:
135
133
  log = log.decode()
136
134
  all_logs.append(log)
137
- yield DockerRunLog(index=i, log=log)
135
+ yield DockerRunLog(log=log)
138
136
  except KeyboardInterrupt:
139
137
  pass
140
138
  else:
@@ -5,6 +5,7 @@ from typing import Any, Generator, List, Optional
5
5
 
6
6
  from pydantic import BaseModel, Field
7
7
 
8
+ from truefoundry.autodeploy.constants import MAX_FILE_SIZE_READ
8
9
  from truefoundry.autodeploy.tools.base import (
9
10
  Event,
10
11
  Message,
@@ -52,15 +53,22 @@ Avoid reading *.lock type files as they tend to be large
52
53
  yield Message(
53
54
  message=f"[bold cyan]Processing:[/] Reading file at [magenta]{request.path}[/] and extracting details..."
54
55
  )
56
+
55
57
  try:
56
- with open(
57
- os.path.join(self.project_root_path, request.path),
58
- "r",
59
- encoding="utf8",
60
- ) as f:
58
+ file_path = os.path.join(self.project_root_path, request.path)
59
+ file_size = os.path.getsize(file_path)
60
+
61
+ if file_size > MAX_FILE_SIZE_READ:
62
+ return ReadFile.Response(error=f"File size exceeds {MAX_FILE_SIZE_READ} bytes limit")
63
+
64
+ with open(file_path, "r", encoding="utf8") as f:
61
65
  response = ReadFile.Response(data=[])
62
66
  for i, line in enumerate(f):
63
67
  response.data.append(Line(line_number=i + 1, content=line))
64
68
  return response
65
69
  except FileNotFoundError as ex:
66
70
  return ReadFile.Response(error=str(ex))
71
+ except UnicodeDecodeError as ex:
72
+ return ReadFile.Response(
73
+ error="This is a binary file, reading it is prohibited."
74
+ )
@@ -0,0 +1,12 @@
1
+ from truefoundry.autodeploy.exception import InvalidRequirementsException
2
+
3
+
4
+ def get_git_binary():
5
+ try:
6
+ import git
7
+
8
+ return git
9
+ except Exception as ex:
10
+ raise InvalidRequirementsException(
11
+ message="We cannot find the 'git' command. We use Git to track changes made while automatically building your project. Please install Git to use this feature or manually create a 'truefoundry.yaml' file."
12
+ ) from ex
@@ -13,10 +13,14 @@ from servicefoundry.cli.config import CliConfig
13
13
  from servicefoundry.cli.const import GROUP_CLS
14
14
  from servicefoundry.cli.util import setup_rich_click
15
15
  from servicefoundry.lib.util import is_debug_env_set, is_experimental_env_set
16
+ from servicefoundry.logger import logger as servicefoundry_logger
16
17
  from servicefoundry.version import __version__
17
18
 
18
19
  from truefoundry.deploy.cli.deploy import deploy_v2_command
19
20
 
21
+ servicefoundry_logger.setLevel(level=logging.INFO)
22
+
23
+
20
24
  click.rich_click.USE_RICH_MARKUP = True
21
25
 
22
26
 
@@ -9,24 +9,20 @@ from click.exceptions import ClickException
9
9
  from servicefoundry.cli.const import GROUP_CLS
10
10
  from servicefoundry.lib.dao import application as application_lib
11
11
 
12
- from truefoundry.autodeploy.exception import GitBinaryNotFoundException
12
+ from truefoundry.autodeploy.cli import cli as autodeploy_cli
13
+ from truefoundry.autodeploy.exception import InvalidRequirementsException
13
14
 
14
- GIT_BINARY = True
15
15
 
16
- try:
17
- from truefoundry.autodeploy.cli import cli as autodeploy_cli
18
- except GitBinaryNotFoundException:
19
- GIT_BINARY = False
20
-
21
- default_file = "default"
22
-
23
-
24
- def _get_yaml_file():
25
- files = ["truefoundry.yaml", "servicefoundry.yaml"]
26
- for file in files:
27
- if os.path.exists(file):
28
- return file
29
- return None
16
+ def _get_default_spec_file():
17
+ paths = [
18
+ "./truefoundry.yaml",
19
+ "./truefoundry.yml",
20
+ "./servicefoundry.yaml",
21
+ "./servicefoundry.yml",
22
+ ]
23
+ for path in paths:
24
+ if os.path.exists(path):
25
+ return path
30
26
 
31
27
 
32
28
  @click.group(
@@ -38,10 +34,10 @@ def _get_yaml_file():
38
34
  @click.option(
39
35
  "-f",
40
36
  "--file",
41
- type=click.STRING,
42
- default=default_file,
37
+ type=click.Path(exists=True, dir_okay=False, resolve_path=True),
38
+ default=_get_default_spec_file(),
43
39
  help="Path to truefoundry.yaml file",
44
- show_default=False,
40
+ show_default=True,
45
41
  )
46
42
  @click.option(
47
43
  "-w",
@@ -67,53 +63,45 @@ def deploy_v2_command(file: str, workspace_fqn: str, wait: bool):
67
63
  except Exception as e:
68
64
  raise ClickException(message=str(e)) from e
69
65
 
70
- if file != default_file and not os.path.exists(file):
71
- raise UsageError(
72
- f"The file {file} does not exist. Please check the file path and try again."
73
- )
74
-
75
- file = _get_yaml_file()
76
-
77
- if file is None:
78
- click.echo(
79
- click.style(
80
- "We did not find any truefoundry.yaml or servicefoundry.yaml at the root path.",
81
- fg="red",
82
- ),
83
- color=True,
84
- )
66
+ if file:
67
+ with open(file, "r") as f:
68
+ application_definition = yaml.safe_load(f)
85
69
 
86
- if not sys.stdout.isatty():
87
- click.echo(
88
- click.style(
89
- 'Please create a truefoundry.yaml or pass the file name with "--file file_name"',
90
- fg="yellow",
91
- ),
92
- color=True,
93
- )
94
- sys.exit(1)
70
+ application = Application.parse_obj(application_definition)
71
+ application.deploy(workspace_fqn=workspace_fqn, wait=wait)
72
+ sys.exit(0)
73
+
74
+ click.echo(
75
+ click.style(
76
+ "We did not find any truefoundry.yaml or servicefoundry.yaml at the root path.",
77
+ fg="red",
78
+ ),
79
+ color=True,
80
+ )
95
81
 
82
+ if not sys.stdout.isatty():
96
83
  click.echo(
97
84
  click.style(
98
- 'We will be using TrueFoundry AI to build your project.\nIf you wish to proceed without TrueFoundry AI,\nyou need to either have a truefoundry.yaml file in your project root or\npass the path to a yaml file using the "--file file_name" option.',
85
+ 'Please create a truefoundry.yaml or pass the file name with "--file file_name"',
99
86
  fg="yellow",
100
87
  ),
88
+ color=True,
101
89
  )
90
+ sys.exit(1)
102
91
 
103
- if GIT_BINARY:
104
- autodeploy_cli(
105
- project_root_path=".", deploy=True, workspace_fqn=workspace_fqn
106
- )
107
- else:
108
- raise UsageError(
109
- "We cannot find the 'git' command. We use Git to track changes made while automatically building your project. Please install Git to use this feature or manually create a 'truefoundry.yaml' file."
110
- )
111
- else:
112
- with open(file, "r") as f:
113
- application_definition = yaml.safe_load(f)
92
+ click.echo(
93
+ click.style(
94
+ 'We will be using TrueFoundry AI to build your project.\nIf you wish to proceed without TrueFoundry AI,\nyou need to either have a truefoundry.yaml file in your project root or\npass the path to a yaml file using the "--file file_name" option.',
95
+ fg="yellow",
96
+ ),
97
+ )
114
98
 
115
- application = Application.parse_obj(application_definition)
116
- application.deploy(workspace_fqn=workspace_fqn, wait=wait)
99
+ try:
100
+ autodeploy_cli(project_root_path=".", deploy=True, workspace_fqn=workspace_fqn)
101
+ except InvalidRequirementsException as e:
102
+ raise UsageError(message=e.message) from e
103
+ except Exception as e:
104
+ raise UsageError(message=str(e)) from e
117
105
 
118
106
 
119
107
  @click.group(
@@ -1,2 +0,0 @@
1
- class GitBinaryNotFoundException(Exception):
2
- pass