truefoundry 0.5.7rc1__py3-none-any.whl → 0.5.8__py3-none-any.whl

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 (40) hide show
  1. truefoundry/cli/__main__.py +2 -2
  2. truefoundry/common/credential_provider.py +1 -1
  3. truefoundry/common/entities.py +15 -15
  4. truefoundry/common/servicefoundry_client.py +7 -2
  5. truefoundry/common/session.py +1 -2
  6. truefoundry/common/types.py +7 -0
  7. truefoundry/common/utils.py +1 -1
  8. truefoundry/deploy/auto_gen/models.py +11 -3
  9. truefoundry/deploy/builder/docker_service.py +1 -1
  10. truefoundry/deploy/cli/commands/delete_command.py +40 -8
  11. truefoundry/deploy/cli/commands/deploy_command.py +3 -5
  12. truefoundry/deploy/lib/clients/servicefoundry_client.py +14 -7
  13. truefoundry/deploy/lib/dao/delete.py +88 -0
  14. truefoundry/deploy/lib/messages.py +1 -0
  15. truefoundry/deploy/lib/model/entity.py +5 -0
  16. truefoundry/deploy/lib/session.py +2 -5
  17. truefoundry/deploy/python_deploy_codegen.py +1 -1
  18. truefoundry/deploy/v2/lib/deploy.py +5 -5
  19. truefoundry/deploy/v2/lib/deploy_workflow.py +8 -5
  20. truefoundry/deploy/v2/lib/deployable_patched_models.py +13 -9
  21. truefoundry/deploy/v2/lib/models.py +1 -1
  22. truefoundry/deploy/v2/lib/patched_models.py +1 -1
  23. truefoundry/deploy/v2/lib/source.py +2 -1
  24. truefoundry/ml/autogen/client/api/health_api.py +1 -2
  25. truefoundry/ml/autogen/client/exceptions.py +1 -1
  26. truefoundry/ml/autogen/models/schema.py +1 -1
  27. truefoundry/ml/log_types/plot.py +2 -2
  28. truefoundry/ml/mlfoundry_run.py +1 -1
  29. truefoundry/ml/session.py +1 -2
  30. truefoundry-0.5.8.dist-info/METADATA +72 -0
  31. {truefoundry-0.5.7rc1.dist-info → truefoundry-0.5.8.dist-info}/RECORD +67 -70
  32. {truefoundry-0.5.7rc1.dist-info → truefoundry-0.5.8.dist-info}/WHEEL +1 -1
  33. truefoundry-0.5.8.dist-info/entry_points.txt +3 -0
  34. truefoundry/workflow/example/deploy.sh +0 -1
  35. truefoundry/workflow/example/hello_world_package/workflow.py +0 -20
  36. truefoundry/workflow/example/package/test_workflow.py +0 -151
  37. truefoundry/workflow/example/truefoundry.yaml +0 -9
  38. truefoundry/workflow/example/workflow.yaml +0 -116
  39. truefoundry-0.5.7rc1.dist-info/METADATA +0 -79
  40. truefoundry-0.5.7rc1.dist-info/entry_points.txt +0 -4
@@ -71,7 +71,7 @@ def truefoundry_cli(ctx, json, debug):
71
71
  logger.add_cli_handler(level=log_level)
72
72
 
73
73
 
74
- def create_truefoundry_cli() -> click.MultiCommand:
74
+ def create_truefoundry_cli() -> click.Group:
75
75
  """Generates CLI by combining all subcommands into a main CLI and returns in
76
76
 
77
77
  Returns:
@@ -105,7 +105,7 @@ def main():
105
105
  try:
106
106
  cli = create_truefoundry_cli()
107
107
  except Exception as e:
108
- raise click.exceptions.UsageError(message=str(e)) from e
108
+ raise click.UsageError(message=str(e)) from e
109
109
  sys.exit(cli())
110
110
 
111
111
 
@@ -38,7 +38,7 @@ class EnvCredentialProvider(CredentialProvider):
38
38
  )
39
39
  self._host = resolve_tfy_host()
40
40
  self._auth_service = AuthServiceClient.from_tfy_host(tfy_host=self._host)
41
- self._token: Token = Token(access_token=api_key, refresh_token=None) # type: ignore[call-arg]
41
+ self._token: Token = Token(access_token=api_key, refresh_token=None)
42
42
 
43
43
  @staticmethod
44
44
  def can_provide() -> bool:
@@ -1,5 +1,4 @@
1
1
  import time
2
- from enum import Enum
3
2
  from typing import Optional
4
3
 
5
4
  import jwt
@@ -8,18 +7,12 @@ from typing_extensions import NotRequired, TypedDict
8
7
  from truefoundry.pydantic_v1 import BaseModel, Field, NonEmptyStr, validator
9
8
 
10
9
 
11
- class UserType(Enum):
12
- user = "user"
13
- serviceaccount = "serviceaccount"
14
-
15
-
16
10
  class UserInfo(BaseModel):
17
11
  class Config:
18
12
  allow_population_by_field_name = True
19
13
  allow_mutation = False
20
14
 
21
15
  user_id: NonEmptyStr
22
- user_type: UserType = UserType.user
23
16
  email: Optional[str] = None
24
17
  tenant_name: NonEmptyStr = Field(alias="tenantName")
25
18
 
@@ -27,9 +20,16 @@ class UserInfo(BaseModel):
27
20
  class _DecodedToken(TypedDict):
28
21
  tenantName: str
29
22
  exp: int
30
- username: str
23
+ username: NotRequired[str]
31
24
  email: NotRequired[str]
32
- userType: UserType
25
+
26
+
27
+ def _user_slug(decoded_token: _DecodedToken) -> str:
28
+ return (
29
+ decoded_token.get("username")
30
+ or decoded_token.get("email")
31
+ or "--user-slug-missing--"
32
+ )
33
33
 
34
34
 
35
35
  class Token(BaseModel):
@@ -55,19 +55,21 @@ class Token(BaseModel):
55
55
 
56
56
  @property
57
57
  def tenant_name(self) -> str:
58
+ assert self.decoded_value is not None
58
59
  return self.decoded_value["tenantName"]
59
60
 
60
61
  def is_going_to_be_expired(self, buffer_in_seconds: int = 120) -> bool:
62
+ assert self.decoded_value is not None
61
63
  exp = int(self.decoded_value["exp"])
62
64
  return (exp - time.time()) < buffer_in_seconds
63
65
 
64
66
  def to_user_info(self) -> UserInfo:
65
- return UserInfo( # type: ignore[call-arg]
66
- user_id=self.decoded_value["username"],
67
+ assert self.decoded_value is not None
68
+ return UserInfo(
69
+ user_id=_user_slug(self.decoded_value),
67
70
  email=self.decoded_value["email"]
68
71
  if "email" in self.decoded_value
69
72
  else None,
70
- user_type=UserType(self.decoded_value.get("userType", UserType.user.value)),
71
73
  tenant_name=self.tenant_name,
72
74
  )
73
75
 
@@ -81,9 +83,7 @@ class CredentialsFileContent(BaseModel):
81
83
  host: NonEmptyStr
82
84
 
83
85
  def to_token(self) -> Token:
84
- return Token( # type: ignore[call-arg]
85
- access_token=self.access_token, refresh_token=self.refresh_token
86
- )
86
+ return Token(access_token=self.access_token, refresh_token=self.refresh_token)
87
87
 
88
88
 
89
89
  class TenantInfo(BaseModel):
@@ -1,9 +1,11 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import functools
4
+ from typing import Callable, TypeVar
4
5
 
5
6
  import requests
6
7
  from packaging import version
8
+ from typing_extensions import ParamSpec
7
9
 
8
10
  from truefoundry.common.constants import (
9
11
  SERVICEFOUNDRY_CLIENT_MAX_RETRIES,
@@ -24,10 +26,13 @@ from truefoundry.common.utils import (
24
26
  from truefoundry.logger import logger
25
27
  from truefoundry.version import __version__
26
28
 
29
+ P = ParamSpec("P")
30
+ R = TypeVar("R")
27
31
 
28
- def check_min_cli_version(fn):
32
+
33
+ def check_min_cli_version(fn: Callable[P, R]) -> Callable[P, R]:
29
34
  @functools.wraps(fn)
30
- def inner(*args, **kwargs):
35
+ def inner(*args: P.args, **kwargs: P.kwargs) -> R:
31
36
  client: "ServiceFoundryServiceClient" = args[0]
32
37
  client.check_min_cli_version()
33
38
  return fn(*args, **kwargs)
@@ -50,10 +50,9 @@ class Session:
50
50
 
51
51
  ACTIVE_SESSION = new_session
52
52
  logger.info(
53
- "Logged in to %r as %r (%s)",
53
+ "Logged in to %r as %r",
54
54
  new_session.tfy_host,
55
55
  new_session.user_info.user_id,
56
- new_session.user_info.email or new_session.user_info.user_type.value,
57
56
  )
58
57
 
59
58
  return ACTIVE_SESSION
@@ -0,0 +1,7 @@
1
+ from typing import Protocol
2
+
3
+
4
+ class UploadCodePackageCallable(Protocol):
5
+ def __call__(
6
+ self, workspace_fqn: str, component_name: str, package_local_path: str
7
+ ) -> str: ...
@@ -53,7 +53,7 @@ def get_tfy_servers_config(tfy_host: str) -> _TFYServersConfig:
53
53
  global _tfy_servers_config
54
54
  if _tfy_servers_config is None:
55
55
  if ENV_VARS.TFY_CLI_LOCAL_DEV_MODE:
56
- _tfy_servers_config = _TFYServersConfig() # type: ignore[call-arg]
56
+ _tfy_servers_config = _TFYServersConfig()
57
57
  else:
58
58
  _tfy_servers_config = _TFYServersConfig.from_tfy_host(tfy_host)
59
59
  return _tfy_servers_config
@@ -1,6 +1,6 @@
1
1
  # generated by datamodel-codegen:
2
2
  # filename: application.json
3
- # timestamp: 2025-01-29T10:40:43+00:00
3
+ # timestamp: 2025-02-19T09:48:05+00:00
4
4
 
5
5
  from __future__ import annotations
6
6
 
@@ -1565,7 +1565,7 @@ class Notebook(BaseWorkbenchInput):
1565
1565
  image: WorkbenchImage
1566
1566
  cull_timeout: conint(ge=5) = Field(
1567
1567
  30,
1568
- description="+label=Stop after (minutes of inactivity)\n+usage=Stop the notebook instance after this much time in minutes of inactivity.\nThe notebook instance will be stopped even if the notebook is open in your browser, but nothing is running on the notebook.\n+sort=5",
1568
+ description='+label=Stop after (minutes of inactivity)\n+usage=Stop the notebook instance after this much time in minutes of inactivity.\nThe notebook instance will be stopped even if the notebook is open in your browser, but nothing is running on the notebook.\n+sort=5\n+uiProps={"descriptionInline":true}',
1569
1569
  )
1570
1570
 
1571
1571
 
@@ -1656,7 +1656,7 @@ class SSHServer(BaseWorkbenchInput):
1656
1656
  )
1657
1657
  cull_timeout: Optional[conint(ge=5)] = Field(
1658
1658
  None,
1659
- description="+label=Stop after (minutes of inactivity)\n+usage=Stop the SSH Server instance after this much time in minutes of inactivity. \\\nThe instance is considered active if there is at least one active SSH connection (a client connected to the SSH server), \\\nor if a background job is running using tmux or screen, or if the pod has restarted.\n+sort=5",
1659
+ description='+label=Stop after (minutes of inactivity)\n+usage=Stop the SSH Server instance after this much time in minutes of inactivity. The instance is considered active if there is at least one active SSH connection (a client connected to the SSH server), or if a background job is running using tmux or screen, or if the pod has restarted.\n+sort=5\n+uiProps={"descriptionInline":true, "warningMessage":"Please note that stop after inactivity is only available for images with tag(including custom images) >= v0.3.10"}',
1660
1660
  )
1661
1661
 
1662
1662
 
@@ -1696,6 +1696,10 @@ class SparkJob(BaseModel):
1696
1696
  None,
1697
1697
  description="+label=Environment Variables\n+usage=Configure environment variables to be injected in the service either as plain text. [Docs](https://docs.truefoundry.com/docs/env-variables)\n+icon=fa-globe\n+sort=21000",
1698
1698
  )
1699
+ conf: Optional[Dict[str, Any]] = Field(
1700
+ None,
1701
+ description="+label=Spark Config Properties\n+usage=Extra configuration properties to be passed to the spark job. [Docs](https://spark.apache.org/docs/latest/configuration.html)\n+icon=fa-gear:#68BBE3\n+sort=21500",
1702
+ )
1699
1703
  mounts: Optional[List[VolumeMount]] = Field(
1700
1704
  None,
1701
1705
  description="+label=Mounts\n+usage=Configure volumes to be mounted to driver and executors. [Docs](https://docs.truefoundry.com/docs/mounting-volumes-job)\n+sort=22000\n+uiType=Mounts",
@@ -1888,6 +1892,10 @@ class ApplicationSet(BaseModel):
1888
1892
  None,
1889
1893
  description="+label=Workspace FQN\n+docs=Fully qualified name of the workspace\n+uiType=Hidden",
1890
1894
  )
1895
+ convert_template_manifest: Optional[bool] = Field(
1896
+ None,
1897
+ description="+label=Convert Template Manifest\n+docs=Flag to indicate if the template manifest should be converted to TrueFoundry manifest\n+uiType=Hidden",
1898
+ )
1891
1899
 
1892
1900
 
1893
1901
  class Application(BaseModel):
@@ -38,7 +38,7 @@ def _catch_error_in_push(response: List[dict]):
38
38
  for line in response:
39
39
  if line.get("error") is not None:
40
40
  raise Exception(
41
- f'Failed to push to registry with message \'{line.get("error")}\''
41
+ f"Failed to push to registry with message '{line.get('error')}'"
42
42
  )
43
43
 
44
44
 
@@ -1,31 +1,63 @@
1
+ from typing import List, Tuple
2
+
1
3
  import rich_click as click
2
4
 
3
5
  from truefoundry.cli.config import CliConfig
6
+ from truefoundry.cli.console import console
4
7
  from truefoundry.cli.const import COMMAND_CLS, GROUP_CLS
5
8
  from truefoundry.cli.display_util import print_json
6
9
  from truefoundry.cli.util import handle_exception_wrapper
7
10
  from truefoundry.deploy.io.rich_output_callback import RichOutputCallBack
11
+ from truefoundry.deploy.lib.clients.servicefoundry_client import (
12
+ ServiceFoundryServiceClient,
13
+ )
8
14
  from truefoundry.deploy.lib.dao import application as application_lib
15
+ from truefoundry.deploy.lib.dao import delete as delete_lib
9
16
  from truefoundry.deploy.lib.dao import workspace as workspace_lib
10
17
  from truefoundry.deploy.lib.messages import (
11
18
  PROMPT_DELETED_APPLICATION,
12
19
  PROMPT_DELETED_WORKSPACE,
20
+ PROMPT_DELETING_MANIFEST,
13
21
  )
22
+ from truefoundry.deploy.lib.model.entity import DeleteResult
14
23
 
15
24
  # TODO (chiragjn): --json should disable all non json console prints
16
25
 
17
26
 
18
- @click.group(name="delete", cls=GROUP_CLS)
19
- def delete_command():
27
+ @click.group(name="delete", cls=GROUP_CLS, invoke_without_command=True)
28
+ @click.pass_context
29
+ @click.option(
30
+ "-f",
31
+ "--file",
32
+ "files",
33
+ type=click.Path(exists=True, dir_okay=False, resolve_path=True),
34
+ help="Path to yaml manifest file (You can pass multiple files at once by providing multiple -f options)",
35
+ show_default=True,
36
+ required=False,
37
+ multiple=True,
38
+ )
39
+ @handle_exception_wrapper
40
+ def delete_command(ctx, files: Tuple[str, ...]):
20
41
  """
21
42
  Delete TrueFoundry resources
22
-
23
- \b
24
- Supported resources:
25
- - Workspace
26
- - Application
27
43
  """
28
- pass
44
+ if ctx.invoked_subcommand is None:
45
+ if not files:
46
+ raise click.UsageError("Missing option '-f' / '--file'")
47
+ delete_results: List[DeleteResult] = []
48
+ client = ServiceFoundryServiceClient()
49
+ for file in files:
50
+ with console.status(PROMPT_DELETING_MANIFEST.format(file), spinner="dots"):
51
+ for delete_result in delete_lib.delete_manifest_file(file, client):
52
+ if delete_result.success:
53
+ console.print(f"[green]\u2714 {delete_result.message}[/]")
54
+ else:
55
+ console.print(f"[red]\u2718 {delete_result.message}[/]")
56
+
57
+ delete_results.append(delete_result)
58
+
59
+ if not all(delete_result.success for delete_result in delete_results):
60
+ raise Exception("Failed to delete one or more resource manifests")
29
61
 
30
62
 
31
63
  @click.command(name="workspace", cls=COMMAND_CLS, help="Delete a Workspace")
@@ -55,7 +55,7 @@ def _get_default_spec_file():
55
55
  help="Wait and tail the deployment progress",
56
56
  )
57
57
  @click.option(
58
- "--force-deploy/--no-force-deploy",
58
+ "--force/--no-force",
59
59
  is_flag=True,
60
60
  show_default=True,
61
61
  default=False,
@@ -68,7 +68,7 @@ def deploy_command(
68
68
  file: str,
69
69
  workspace_fqn: Optional[str],
70
70
  wait: bool,
71
- force_deploy: bool,
71
+ force: bool = False,
72
72
  ):
73
73
  if ctx.invoked_subcommand is not None:
74
74
  return
@@ -85,9 +85,7 @@ def deploy_command(
85
85
  application_definition = yaml.safe_load(f)
86
86
 
87
87
  application = Application.parse_obj(application_definition)
88
- application.deploy(
89
- workspace_fqn=workspace_fqn, wait=wait, force_deploy=force_deploy
90
- )
88
+ application.deploy(workspace_fqn=workspace_fqn, wait=wait, force=force)
91
89
  sys.exit(0)
92
90
 
93
91
  click.echo(
@@ -4,7 +4,7 @@ import json
4
4
  import os
5
5
  import time
6
6
  from datetime import datetime, timezone
7
- from typing import TYPE_CHECKING, Any, Dict, List, Optional
7
+ from typing import Any, Dict, List, Optional
8
8
  from urllib.parse import urljoin
9
9
 
10
10
  import requests
@@ -50,9 +50,6 @@ DEPLOYMENT_LOGS_SUBSCRIBE_MESSAGE = "DEPLOYMENT_LOGS"
50
50
  BUILD_LOGS_SUBSCRIBE_MESSAGE = "BUILD_LOGS"
51
51
  MAX_RETRIES_WORKFLOW_TRIGGER = 3
52
52
 
53
- if TYPE_CHECKING:
54
- from truefoundry.deploy.auto_gen.models import Application
55
-
56
53
 
57
54
  def _upload_packaged_code(metadata, package_file):
58
55
  file_size = os.stat(package_file).st_size
@@ -242,13 +239,13 @@ class ServiceFoundryServiceClient(BaseServiceFoundryServiceClient):
242
239
  self,
243
240
  workspace_id: str,
244
241
  application: auto_gen_models.Workflow,
245
- force_deploy: bool,
242
+ force: bool = False,
246
243
  ) -> Deployment:
247
244
  data = {
248
245
  "workspaceId": workspace_id,
249
246
  "name": application.name,
250
247
  "manifest": application.dict(exclude_none=True),
251
- "forceDeploy": force_deploy,
248
+ "forceDeploy": force,
252
249
  }
253
250
  logger.debug(json.dumps(data))
254
251
  url = f"{self._api_server_url}/{VERSION_PREFIX}/deployment"
@@ -265,7 +262,7 @@ class ServiceFoundryServiceClient(BaseServiceFoundryServiceClient):
265
262
  time_obj.replace(tzinfo=timezone.utc)
266
263
  local_time = time_obj.astimezone(tzlocal())
267
264
  local_time_str = local_time.isoformat()
268
- return f'[{local_time_str}] {log["log"].strip()}'
265
+ return f"[{local_time_str}] {log['log'].strip()}"
269
266
 
270
267
  def _tail_logs(
271
268
  self,
@@ -696,6 +693,16 @@ class ServiceFoundryServiceClient(BaseServiceFoundryServiceClient):
696
693
  response_data = request_handling(response)
697
694
  return response_data
698
695
 
696
+ @check_min_cli_version
697
+ def delete(self, manifest: Dict[str, Any]):
698
+ url = f"{self._api_server_url}/{VERSION_PREFIX}/delete"
699
+ body = {"manifest": manifest}
700
+ response = session_with_retries().post(
701
+ url, headers=self._get_header(), json=body
702
+ )
703
+ response_data = request_handling(response)
704
+ return response_data
705
+
699
706
  def terminate_job_run(
700
707
  self,
701
708
  deployment_id: str,
@@ -0,0 +1,88 @@
1
+ from pathlib import Path
2
+ from typing import Any, Dict, Iterator, Optional
3
+
4
+ import yaml
5
+
6
+ from truefoundry.deploy.lib.clients.servicefoundry_client import (
7
+ ServiceFoundryServiceClient,
8
+ )
9
+ from truefoundry.deploy.lib.model.entity import DeleteResult, Manifest
10
+ from truefoundry.pydantic_v1 import ValidationError
11
+
12
+
13
+ def _delete_manifest(
14
+ manifest: Dict[str, Any],
15
+ client: Optional[ServiceFoundryServiceClient] = None,
16
+ filename: Optional[str] = None,
17
+ index: Optional[int] = None,
18
+ ) -> DeleteResult:
19
+ client = client or ServiceFoundryServiceClient()
20
+
21
+ file_metadata = ""
22
+ if index is not None:
23
+ file_metadata += f" at index {index}"
24
+ if filename:
25
+ file_metadata += f" from file {filename}"
26
+
27
+ try:
28
+ manifest = Manifest.parse_obj(manifest)
29
+ except ValidationError as ex:
30
+ return DeleteResult(
31
+ success=False,
32
+ message=f"Failed to parse manifest{file_metadata}. Error: {ex}",
33
+ )
34
+
35
+ try:
36
+ client.delete(manifest.dict())
37
+
38
+ return DeleteResult(
39
+ success=True,
40
+ message=(
41
+ f"Successfully deleted resource manifest {manifest.name} of type {manifest.type}."
42
+ ),
43
+ )
44
+ except Exception as ex:
45
+ return DeleteResult(
46
+ success=False,
47
+ message=(
48
+ f"Failed to delete resource manifest {manifest.name} of type {manifest.type}. Error: {ex}."
49
+ ),
50
+ )
51
+
52
+
53
+ def delete_manifest(
54
+ manifest: Dict[str, Any],
55
+ client: Optional[ServiceFoundryServiceClient] = None,
56
+ ) -> DeleteResult:
57
+ return _delete_manifest(manifest=manifest, client=client)
58
+
59
+
60
+ def delete_manifest_file(
61
+ filepath: str,
62
+ client: Optional[ServiceFoundryServiceClient] = None,
63
+ ) -> Iterator[DeleteResult]:
64
+ client = client or ServiceFoundryServiceClient()
65
+ filename = Path(filepath).name
66
+ try:
67
+ with open(filepath, "r") as f:
68
+ manifests_it = list(yaml.safe_load_all(f))
69
+ except Exception as ex:
70
+ yield DeleteResult(
71
+ success=False,
72
+ message=f"Failed to read file {filepath} as a valid YAML file. Error: {ex}",
73
+ )
74
+ else:
75
+ for index, manifest in enumerate(manifests_it):
76
+ if not isinstance(manifest, dict):
77
+ yield DeleteResult(
78
+ success=False,
79
+ message=f"Failed to delete resource manifest at index {index} from file {filename}. Error: A manifest must be a dict, got type {type(manifest)}",
80
+ )
81
+ continue
82
+
83
+ yield _delete_manifest(
84
+ manifest=manifest,
85
+ client=client,
86
+ filename=filename,
87
+ index=index,
88
+ )
@@ -10,3 +10,4 @@ PROMPT_NO_WORKSPACES = f"""[yellow]No workspaces found. Either cluster name is w
10
10
  PROMPT_NO_APPLICATIONS = f"""[yellow]No applications found. You can create one with [bold]{TFY} deploy[/] from within your application folder"""
11
11
  PROMPT_NO_VERSIONS = """[yellow]No application versions found."""
12
12
  PROMPT_APPLYING_MANIFEST = """[yellow]Applying manifest for file {!r}[/]"""
13
+ PROMPT_DELETING_MANIFEST = """[yellow]Deleting manifest for file {!r}[/]"""
@@ -280,6 +280,11 @@ class ApplyResult(BaseModel):
280
280
  message: str
281
281
 
282
282
 
283
+ class DeleteResult(BaseModel):
284
+ success: bool
285
+ message: str
286
+
287
+
283
288
  class Manifest(Base):
284
289
  type: str
285
290
  name: str
@@ -57,10 +57,9 @@ def login(
57
57
  return login(api_key=api_key, host=host, relogin=True)
58
58
 
59
59
  user_info = cred_file_content.to_token().to_user_info()
60
- user_name_display_info = user_info.email or user_info.user_type.value
61
60
  output_hook.print_line(
62
61
  relogin_error_message(
63
- f"Already logged in to {cred_file_content.host!r} as {user_info.user_id!r} ({user_name_display_info})",
62
+ f"Already logged in to {cred_file_content.host!r} as {user_info.user_id!r}",
64
63
  host=host,
65
64
  )
66
65
  )
@@ -81,10 +80,8 @@ def login(
81
80
  cred_file.write(cred_file_content)
82
81
 
83
82
  user_info = token.to_user_info()
84
- user_name_display_info = user_info.email or user_info.user_type.value
85
83
  output_hook.print_line(
86
- f"Successfully logged in to {cred_file_content.host!r} as "
87
- f"{user_info.user_id!r} ({user_name_display_info})"
84
+ f"Successfully logged in to {cred_file_content.host!r} as {user_info.user_id!r}"
88
85
  )
89
86
  return True
90
87
 
@@ -27,7 +27,7 @@ logging.basicConfig(level=logging.INFO)
27
27
 
28
28
  {application_type} = {spec_repr}
29
29
 
30
- {application_type}.deploy(workspace_fqn="{workspace_fqn}")\
30
+ {application_type}.deploy(workspace_fqn="{workspace_fqn}", wait=False)\
31
31
  """
32
32
  return code
33
33
 
@@ -214,8 +214,8 @@ No Workspace FQN was provided or mentioned in the spec.
214
214
  Either add a `workspace_fqn` to your yaml spec as
215
215
 
216
216
  ```
217
- name: {getattr(component, 'name', 'my-app')}
218
- type: {getattr(component, 'type', 'undefined')}
217
+ name: {getattr(component, "name", "my-app")}
218
+ type: {getattr(component, "type", "undefined")}
219
219
  ...
220
220
  workspace_fqn: <your workspace fqn>
221
221
  ```
@@ -224,7 +224,7 @@ or Python deployment spec as
224
224
 
225
225
  ```
226
226
  app = {component.__class__.__name__}(
227
- name='{getattr(component, 'name', 'my-app')}',
227
+ name='{getattr(component, "name", "my-app")}',
228
228
  ...
229
229
  workspace_fqn='<your workspace fqn>'
230
230
  )
@@ -252,7 +252,7 @@ def deploy_component(
252
252
  component: Component,
253
253
  workspace_fqn: Optional[str] = None,
254
254
  wait: bool = True,
255
- force_deploy: bool = False,
255
+ force: bool = False,
256
256
  ) -> Deployment:
257
257
  _warn_when_gpu_selected_without_cuda(component=component)
258
258
  workspace_fqn = _resolve_workspace_fqn(
@@ -274,7 +274,7 @@ def deploy_component(
274
274
  response = client.deploy_application(
275
275
  workspace_id=workspace_id,
276
276
  application=updated_component,
277
- force_deploy=force_deploy,
277
+ force=force,
278
278
  )
279
279
  logger.info(
280
280
  "🚀 Deployment started for application '%s'. Deployment FQN is '%s'.",
@@ -1,7 +1,7 @@
1
1
  import os
2
2
  import sys
3
3
  from pathlib import Path
4
- from typing import Callable, Dict, List, Optional, Union
4
+ from typing import Dict, List, Optional, Union
5
5
 
6
6
  import requirements
7
7
  from flytekit.configuration import (
@@ -19,6 +19,7 @@ from flytekit.tools.translator import TaskSpec as FlyteTaskSpec
19
19
  from flytekit.tools.translator import WorkflowSpec as FlyteWorkflowSpec
20
20
  from google.protobuf.json_format import MessageToDict
21
21
 
22
+ from truefoundry.common.types import UploadCodePackageCallable
22
23
  from truefoundry.deploy.auto_gen import models as auto_gen_models
23
24
  from truefoundry.deploy.lib.clients.servicefoundry_client import (
24
25
  ServiceFoundryServiceClient,
@@ -39,7 +40,7 @@ from truefoundry.workflow.workflow import (
39
40
  def _handle_code_upload_for_workflow(
40
41
  workflow: auto_gen_models.Workflow,
41
42
  workspace_fqn: str,
42
- upload_code_package: Callable[[str, str, str], str],
43
+ upload_code_package: UploadCodePackageCallable,
43
44
  ) -> auto_gen_models.Workflow:
44
45
  new_workflow = workflow.copy(deep=True)
45
46
  new_workflow.source = local_source_to_remote_source(
@@ -92,7 +93,7 @@ def _is_tfy_wf_present_in_task_python_build(
92
93
 
93
94
 
94
95
  def _is_dynamic_task(flyte_task: FlyteTaskSpec) -> bool:
95
- envs: Dict[str:str] = flyte_task.template.container.env or {}
96
+ envs: Dict[str, str] = flyte_task.template.container.env or {}
96
97
  return SERIALIZED_CONTEXT_ENV_VAR in envs.keys()
97
98
 
98
99
 
@@ -273,7 +274,7 @@ def deploy_workflow(
273
274
  workflow: auto_gen_models.Workflow,
274
275
  workspace_fqn: str,
275
276
  wait: bool = True,
276
- force_deploy: bool = False,
277
+ force: bool = False,
277
278
  ) -> Deployment:
278
279
  _generate_manifest_for_workflow(workflow)
279
280
  _validate_workspace_fqn(workflow, workspace_fqn)
@@ -296,7 +297,9 @@ def deploy_workflow(
296
297
  )
297
298
 
298
299
  deployment = client.deploy_application(
299
- workspace_id=workspace_id, application=workflow
300
+ workspace_id=workspace_id,
301
+ application=workflow,
302
+ force=force,
300
303
  )
301
304
  logger.info(
302
305
  "🚀 Deployment started for application '%s'. Deployment FQN is '%s'.",