|
15 | 15 | BaseApplyConfigurator, |
16 | 16 | ) |
17 | 17 | from dstack._internal.cli.services.profile import apply_profile_args, register_profile_args |
| 18 | +from dstack._internal.cli.services.repos import init_default_virtual_repo |
18 | 19 | from dstack._internal.cli.utils.common import ( |
19 | 20 | confirm_ask, |
20 | 21 | console, |
| 22 | + warn, |
21 | 23 | ) |
22 | 24 | from dstack._internal.cli.utils.rich import MultiItemStatus |
23 | 25 | from dstack._internal.cli.utils.run import get_runs_table, print_run_plan |
|
40 | 42 | TaskConfiguration, |
41 | 43 | ) |
42 | 44 | from dstack._internal.core.models.repos.base import Repo |
| 45 | +from dstack._internal.core.models.repos.local import LocalRepo |
43 | 46 | from dstack._internal.core.models.resources import CPUSpec |
44 | 47 | from dstack._internal.core.models.runs import JobStatus, JobSubmission, RunSpec, RunStatus |
45 | 48 | from dstack._internal.core.services.configs import ConfigManager |
@@ -76,17 +79,42 @@ def apply_configuration( |
76 | 79 | self.apply_args(conf, configurator_args, unknown_args) |
77 | 80 | self.validate_gpu_vendor_and_image(conf) |
78 | 81 | self.validate_cpu_arch_and_image(conf) |
79 | | - if repo is None: |
80 | | - repo = self.api.repos.load(Path.cwd()) |
81 | 82 | config_manager = ConfigManager() |
82 | | - if repo.repo_dir is not None: |
83 | | - repo_config = config_manager.get_repo_config_or_error(repo.repo_dir) |
84 | | - self.api.ssh_identity_file = repo_config.ssh_key_path |
85 | | - else: |
86 | | - self.api.ssh_identity_file = get_ssh_keypair( |
87 | | - command_args.ssh_identity_file, |
88 | | - config_manager.dstack_key_path, |
89 | | - ) |
| 83 | + if repo is None: |
| 84 | + repo_path = Path.cwd() |
| 85 | + repo_config = config_manager.get_repo_config(repo_path) |
| 86 | + if repo_config is None: |
| 87 | + warn( |
| 88 | + "The repo is not initialized. Starting from 0.19.25, repos are optional\n" |
| 89 | + "There are three options:\n" |
| 90 | + " - Run `dstack init` to initialize the current directory as a repo\n" |
| 91 | + " - Specify `--repo`\n" |
| 92 | + " - Specify `--no-repo` to not use any repo and supress this warning" |
| 93 | + " (this will be the default in the future versions)" |
| 94 | + ) |
| 95 | + if not command_args.yes and not confirm_ask("Continue without the repo?"): |
| 96 | + console.print("\nExiting...") |
| 97 | + return |
| 98 | + repo = init_default_virtual_repo(self.api) |
| 99 | + else: |
| 100 | + # Unlikely, but may raise ConfigurationError if the repo does not exist |
| 101 | + # on the server side (stale entry in `config.yml`) |
| 102 | + repo = self.api.repos.load(repo_path) |
| 103 | + if isinstance(repo, LocalRepo): |
| 104 | + warn( |
| 105 | + f"{repo.repo_dir} is a local repo.\n" |
| 106 | + "Local repos are deprecated since 0.19.25" |
| 107 | + " and will be removed soon\n" |
| 108 | + "There are two options:\n" |
| 109 | + " - Migrate to `files`: https://dstack.ai/docs/concepts/tasks/#files\n" |
| 110 | + " - Specify `--no-repo` if you don't need the repo at all\n" |
| 111 | + "In either case, you can run `dstack init --remove` to remove the repo" |
| 112 | + " (only the record about the repo, not its files) and this warning" |
| 113 | + ) |
| 114 | + self.api.ssh_identity_file = get_ssh_keypair( |
| 115 | + command_args.ssh_identity_file, |
| 116 | + config_manager.dstack_key_path, |
| 117 | + ) |
90 | 118 | profile = load_profile(Path.cwd(), configurator_args.profile) |
91 | 119 | with console.status("Getting apply plan..."): |
92 | 120 | run_plan = self.api.runs.get_run_plan( |
|
0 commit comments