11import argparse
22import os
33from pathlib import Path
4+ from typing import Optional
45
56from dstack ._internal .cli .commands import BaseCommand
6- from dstack ._internal .cli .services .repos import init_repo , register_init_repo_args
7+ from dstack ._internal .cli .services .repos import (
8+ get_repo_from_dir ,
9+ get_repo_from_url ,
10+ is_git_repo_url ,
11+ register_init_repo_args ,
12+ )
713from dstack ._internal .cli .utils .common import configure_logging , confirm_ask , console , warn
814from dstack ._internal .core .errors import ConfigurationError
9- from dstack ._internal .core .models .repos .base import RepoType
1015from dstack ._internal .core .services .configs import ConfigManager
1116from dstack .api import Client
1217
@@ -21,6 +26,15 @@ def _register(self):
2126 help = "The name of the project" ,
2227 default = os .getenv ("DSTACK_PROJECT" ),
2328 )
29+ self ._parser .add_argument (
30+ "-P" ,
31+ "--repo" ,
32+ help = (
33+ "The repo to initialize. Can be a local path or a Git repo URL."
34+ " Defaults to the current working directory."
35+ ),
36+ dest = "repo" ,
37+ )
2438 register_init_repo_args (self ._parser )
2539 # Deprecated since 0.19.25, ignored
2640 self ._parser .add_argument (
@@ -30,7 +44,7 @@ def _register(self):
3044 type = Path ,
3145 dest = "ssh_identity_file" ,
3246 )
33- # A hidden mode for transitional period only, remove it with local repos
47+ # A hidden mode for transitional period only, remove it with repos in `config.yml`
3448 self ._parser .add_argument (
3549 "--remove" ,
3650 help = argparse .SUPPRESS ,
@@ -39,44 +53,62 @@ def _register(self):
3953
4054 def _command (self , args : argparse .Namespace ):
4155 configure_logging ()
56+
57+ repo_path : Optional [Path ] = None
58+ repo_url : Optional [str ] = None
59+ repo_arg : Optional [str ] = args .repo
60+ if repo_arg is not None :
61+ if is_git_repo_url (repo_arg ):
62+ repo_url = repo_arg
63+ else :
64+ repo_path = Path (repo_arg ).expanduser ().resolve ()
65+ else :
66+ repo_path = Path .cwd ()
67+
4268 if args .remove :
69+ if repo_url is not None :
70+ raise ConfigurationError (f"Local path expected, got URL: { repo_url } " )
71+ assert repo_path is not None
4372 config_manager = ConfigManager ()
44- repo_path = Path .cwd ()
4573 repo_config = config_manager .get_repo_config (repo_path )
4674 if repo_config is None :
47- raise ConfigurationError ("The repo is not initialized, nothing to remove" )
48- if repo_config .repo_type != RepoType .LOCAL :
49- raise ConfigurationError ("`dstack init --remove` is for local repos only" )
75+ raise ConfigurationError ("Repo record not found, nothing to remove" )
5076 console .print (
51- f"You are about to remove the local repo { repo_path } \n "
77+ f"You are about to remove the repo { repo_path } \n "
5278 "Only the record about the repo will be removed,"
5379 " the repo files will remain intact\n "
5480 )
55- if not confirm_ask ("Remove the local repo?" ):
81+ if not confirm_ask ("Remove the repo?" ):
5682 return
5783 config_manager .delete_repo_config (repo_config .repo_id )
5884 config_manager .save ()
59- console .print ("Local repo has been removed" )
85+ console .print ("Repo has been removed" )
6086 return
61- api = Client .from_config (
62- project_name = args .project , ssh_identity_file = args .ssh_identity_file
63- )
64- if args .local :
87+
88+ local : bool = args .local
89+ if local :
6590 warn (
66- "Local repos are deprecated since 0.19.25 and will be removed soon."
67- " Consider using ` files` instead: https://dstack.ai/docs/concepts/tasks/#files"
91+ "Local repos are deprecated since 0.19.25 and will be removed soon. Consider "
92+ " using [code] files[/code] instead: https://dstack.ai/docs/concepts/tasks/#files"
6893 )
6994 if args .ssh_identity_file :
7095 warn (
71- "`--ssh-identity` in `dstack init` is deprecated and ignored since 0.19.25."
72- " Use this option with `dstack apply` and `dstack attach` instead"
96+ "[code]--ssh-identity[/code] in [code]dstack init[/code] is deprecated and ignored"
97+ " since 0.19.25. Use this option with [code]dstack apply[/code]"
98+ " and [code]dstack attach[/code] instead"
7399 )
74- init_repo (
75- api = api ,
76- repo_path = Path .cwd (),
77- repo_branch = None ,
78- repo_hash = None ,
79- local = args .local ,
100+
101+ if repo_url is not None :
102+ # Dummy repo branch to avoid autodetection that fails on private repos.
103+ # We don't need branch/hash for repo_id anyway.
104+ repo = get_repo_from_url (repo_url , repo_branch = "master" )
105+ elif repo_path is not None :
106+ repo = get_repo_from_dir (repo_path , local = local )
107+ else :
108+ assert False , "should not reach here"
109+ api = Client .from_config (project_name = args .project )
110+ api .repos .init (
111+ repo = repo ,
80112 git_identity_file = args .git_identity_file ,
81113 oauth_token = args .gh_token ,
82114 )
0 commit comments