Coverage for .nox/test-3-12/lib/python3.12/site-packages/nskit/vcs/providers/azure_devops.py: 12%
56 statements
« prev ^ index » next coverage.py v7.3.3, created at 2023-12-19 17:42 +0000
« prev ^ index » next coverage.py v7.3.3, created at 2023-12-19 17:42 +0000
1"""Azure Devops provider using azure-cli to manage it."""
2from io import StringIO
3import json
4from typing import List
6try:
7 from azure.cli.core import get_default_cli
8except ImportError:
9 raise ImportError('Azure Devops Provider requires installing extra dependencies, use pip install nskit[azure_devops]')
11from pydantic import HttpUrl
12from pydantic_settings import SettingsConfigDict
14from nskit.vcs.providers.abstract import RepoClient, VCSProviderSettings
16# Want to use MS interactive Auth as default, but can't get it working, instead, using cli invoke
19class AzureDevOpsSettings(VCSProviderSettings):
20 """Azure DevOps settings."""
21 model_config = SettingsConfigDict(env_prefix='AZURE_DEVOPS_', env_file='.env')
22 url: HttpUrl = "https://dev.azure.com"
23 organisation: str
24 project: str
26 @property
27 def organisation_url(self):
28 """Get the organistion Url."""
29 return f'{self.url}/{self.organisation}'
31 @property
32 def project_url(self):
33 """Get the project url."""
34 return f'{self.organisation_url}/{self.project}'
36 @property
37 def repo_client(self) -> 'AzureDevOpsRepoClient':
38 """Get the instantiated repo client."""
39 return AzureDevOpsRepoClient(self)
42class AzureDevOpsRepoClient(RepoClient):
43 """Client for managing Azure DevOps repos using azure-cli."""
45 def __init__(self, config: AzureDevOpsSettings):
46 """Initialise the client."""
47 self._cli = get_default_cli()
48 self._config = config
50 def _invoke(self, command, out_file=None):
51 return self._cli.invoke(command, out_file=out_file)
53 def check_exists(self, repo_name: str) -> bool:
54 """Check if the repo exists in the project."""
55 output = StringIO()
56 return not self._invoke(['repos',
57 'show',
58 '--organization',
59 self._config.organisation_url,
60 '--project',
61 self._config.project,
62 '-r',
63 repo_name],
64 out_file=output)
66 def create(self, repo_name: str):
67 """Create the repo in the project."""
68 output = StringIO()
69 return self._invoke(['repos',
70 'create',
71 '--organization',
72 self._config.organisation_url,
73 '--project',
74 self._config.project,
75 '--name',
76 repo_name],
77 out_file=output)
79 def delete(self, repo_name: str):
80 """Delete the repo if it exists in the project."""
81 # We need to get the ID
82 show_output = StringIO()
83 result = self._invoke(['repos',
84 'show',
85 '--organization',
86 self._config.organisation_url,
87 '--project',
88 self._config.project,
89 '-r',
90 repo_name],
91 out_file=show_output)
92 if not result:
93 # Exists
94 repo_info = json.loads(show_output.getvalue())
95 repo_id = repo_info['id']
96 output = StringIO()
97 return self._invoke(['repos',
98 'delete',
99 '--organization',
100 self._config.organisation_url,
101 '--project',
102 self._config.project,
103 '--id',
104 repo_id],
105 out_file=output)
107 def get_remote_url(self, repo_name: str) -> HttpUrl:
108 """Get the remote url for the repo."""
109 output = StringIO()
110 result = self._invoke(['repos',
111 'show',
112 '--organization',
113 self._config.organisation_url,
114 '--project',
115 self._config.project,
116 '-r',
117 repo_name],
118 out_file=output)
119 if not result:
120 # Exists
121 repo_info = json.loads(output.getvalue())
122 return repo_info['remoteUrl']
124 def list(self) -> List[str]:
125 """List the repos in the project."""
126 output = StringIO()
127 result = self._invoke(['repos',
128 'list',
129 '--organization',
130 self._config.organisation_url,
131 '--project',
132 self._config.project],
133 out_file=output)
134 if not result:
135 # Exists
136 repo_list = [u['name'] for u in json.loads(output.getvalue())]
137 return repo_list