devopsdriver 0.1.43__tar.gz → 0.1.45__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.
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/PKG-INFO +5 -5
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/README.md +1 -1
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/__init__.py +1 -1
- devopsdriver-0.1.45/devopsdriver/azdo/builds/build.py +74 -0
- devopsdriver-0.1.45/devopsdriver/azdo/builds/client.py +59 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/azdo/clients.py +17 -4
- devopsdriver-0.1.45/devopsdriver/azdo/pipeline/__init__.py +12 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/azdo/pipeline/run.py +1 -1
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/azdo/workitem/client.py +3 -2
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/dataobject.py +9 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver.egg-info/PKG-INFO +5 -5
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver.egg-info/SOURCES.txt +5 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver.egg-info/requires.txt +2 -2
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/pyproject.toml +3 -3
- devopsdriver-0.1.45/tests/test_azure_build.py +99 -0
- devopsdriver-0.1.45/tests/test_azure_build_client.py +76 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/tests/test_azure_clients.py +32 -7
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/tests/test_azure_pipeline_run.py +1 -1
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/tests/test_azure_workitem_client.py +1 -1
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/LICENSE +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/azdo/__init__.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/azdo/azureobject.py +0 -0
- {devopsdriver-0.1.43/devopsdriver/azdo/pipeline → devopsdriver-0.1.45/devopsdriver/azdo/builds}/__init__.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/azdo/pipeline/client.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/azdo/pipeline/log.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/azdo/pipeline/pipeline.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/azdo/timestamp.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/azdo/workitem/__init__.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/azdo/workitem/wiql.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/manage_settings.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/sendmail.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/settings.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/template.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver/templates/manage_settings.txt.mako +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver.egg-info/dependency_links.txt +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver.egg-info/entry_points.txt +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/devopsdriver.egg-info/top_level.txt +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/setup.cfg +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/tests/test_azure_azureobject.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/tests/test_azure_pipeline.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/tests/test_azure_pipeline_client.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/tests/test_azure_timestamp.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/tests/test_azure_workitem_wiql.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/tests/test_dataobject.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/tests/test_manage_settings.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/tests/test_sendmail.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/tests/test_settings.py +0 -0
- {devopsdriver-0.1.43 → devopsdriver-0.1.45}/tests/test_template.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: devopsdriver
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.45
|
|
4
4
|
Summary: DevOps tools
|
|
5
5
|
Author-email: Marc Page <marcallenpage@gmail.com>
|
|
6
6
|
License: This is free and unencumbered software released into the public domain.
|
|
@@ -40,7 +40,7 @@ Classifier: Intended Audience :: Developers
|
|
|
40
40
|
Classifier: Programming Language :: Python
|
|
41
41
|
Classifier: Programming Language :: Python :: 3
|
|
42
42
|
Classifier: License :: OSI Approved :: The Unlicense (Unlicense)
|
|
43
|
-
Classifier: Programming Language :: Python :: 3.
|
|
43
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
44
44
|
Classifier: Topic :: Utilities
|
|
45
45
|
Classifier: Topic :: Software Development
|
|
46
46
|
Requires-Python: >=3.10
|
|
@@ -48,9 +48,9 @@ Description-Content-Type: text/markdown
|
|
|
48
48
|
License-File: LICENSE
|
|
49
49
|
Requires-Dist: PyYAML==6.0.1
|
|
50
50
|
Requires-Dist: keyring==25.1.0
|
|
51
|
-
Requires-Dist: setuptools==69.
|
|
51
|
+
Requires-Dist: setuptools==69.5.1
|
|
52
52
|
Requires-Dist: azure-devops==7.1.0b4
|
|
53
|
-
Requires-Dist: Mako==1.3.
|
|
53
|
+
Requires-Dist: Mako==1.3.3
|
|
54
54
|
Provides-Extra: dev
|
|
55
55
|
Requires-Dist: black>=24.3.0; extra == "dev"
|
|
56
56
|
Requires-Dist: pylint>=3.1.0; extra == "dev"
|
|
@@ -60,7 +60,7 @@ Requires-Dist: coverage>=7.4.4; extra == "test"
|
|
|
60
60
|
Provides-Extra: doc
|
|
61
61
|
|
|
62
62
|

|
|
63
|
-
[](https://pypi.org/project/devopsdriver/0.1.45/)
|
|
64
64
|
[](https://github.com/marcpage/devops-driver?tab=Unlicense-1-ov-file#readme)
|
|
65
65
|
[](https://github.com/marcpage/devops-driver/graphs/contributors)
|
|
66
66
|
[](http://makeapullrequest.com)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|

|
|
2
|
-
[](https://pypi.org/project/devopsdriver/0.1.45/)
|
|
3
3
|
[](https://github.com/marcpage/devops-driver?tab=Unlicense-1-ov-file#readme)
|
|
4
4
|
[](https://github.com/marcpage/devops-driver/graphs/contributors)
|
|
5
5
|
[](http://makeapullrequest.com)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
""" Azure Build """
|
|
4
|
+
|
|
5
|
+
from azure.devops.v7_1.build.models import Build as AzureBuild
|
|
6
|
+
from azure.devops.v7_1.build import BuildClient
|
|
7
|
+
from azure.devops.v7_1.build.models import TimelineRecord
|
|
8
|
+
|
|
9
|
+
from devopsdriver.azdo import AzureObject
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Build(AzureObject): # pylint: disable=too-few-public-methods
|
|
13
|
+
"""Azure Build"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, client: BuildClient, build: AzureBuild):
|
|
16
|
+
self.client = client
|
|
17
|
+
self.build = build
|
|
18
|
+
super().__init__(build)
|
|
19
|
+
|
|
20
|
+
class Step(AzureObject): # pylint: disable=too-few-public-methods
|
|
21
|
+
"""Azure Build Step (job, task, step)"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, entry: TimelineRecord):
|
|
24
|
+
self.children = []
|
|
25
|
+
self.log_contents: str = None
|
|
26
|
+
super().__init__(entry)
|
|
27
|
+
|
|
28
|
+
def all_logs(self) -> list[str]:
|
|
29
|
+
"""Get a list of all logs in chronological order
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
list[str]: List of all the logs
|
|
33
|
+
"""
|
|
34
|
+
logs = [] if self.log_contents is None else [self.log_contents]
|
|
35
|
+
|
|
36
|
+
for child in self.children:
|
|
37
|
+
logs.extend(child.all_logs())
|
|
38
|
+
|
|
39
|
+
return logs
|
|
40
|
+
|
|
41
|
+
def __add_steps_and_logs(self, entry: Step, to_process: list[Step]) -> Step:
|
|
42
|
+
project = self.build.project.name
|
|
43
|
+
build_id = self.build.id
|
|
44
|
+
|
|
45
|
+
if entry.log:
|
|
46
|
+
entry.log_contents = "\n".join(
|
|
47
|
+
self.client.get_build_log_lines(project, build_id, entry.log.id)
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
entry.children = [e for e in to_process if e.parent_id == entry.id]
|
|
51
|
+
|
|
52
|
+
for child in entry.children:
|
|
53
|
+
to_process.remove(child)
|
|
54
|
+
self.__add_steps_and_logs(child, to_process)
|
|
55
|
+
|
|
56
|
+
return entry
|
|
57
|
+
|
|
58
|
+
def get_logs(self) -> Step:
|
|
59
|
+
"""Gets the logs in a hierarchical structure
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Step: The root element of the log hierarchy
|
|
63
|
+
"""
|
|
64
|
+
project = self.build.project.name
|
|
65
|
+
build_id = self.build.id
|
|
66
|
+
steps = [
|
|
67
|
+
Build.Step(r)
|
|
68
|
+
for r in self.client.get_build_timeline(project, build_id).records
|
|
69
|
+
]
|
|
70
|
+
root = [s for s in steps if not s.parent_id]
|
|
71
|
+
assert len(root) == 1, root
|
|
72
|
+
root = root[0]
|
|
73
|
+
steps.remove(root)
|
|
74
|
+
return self.__add_steps_and_logs(root, steps)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
""" Azure Build Client """
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from azure.devops.v7_1.build import BuildClient
|
|
9
|
+
|
|
10
|
+
from .build import Build
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Client: # pylint: disable=too-few-public-methods
|
|
14
|
+
"""The Build client"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, client: BuildClient):
|
|
17
|
+
self.client = client
|
|
18
|
+
|
|
19
|
+
def list( # pylint: disable=too-many-arguments
|
|
20
|
+
self,
|
|
21
|
+
project: str,
|
|
22
|
+
pipelines: list[int] = None,
|
|
23
|
+
start: datetime = None,
|
|
24
|
+
end: datetime = None,
|
|
25
|
+
status: str = None,
|
|
26
|
+
result: str = None,
|
|
27
|
+
properties: list[str] = None,
|
|
28
|
+
branch: str = None,
|
|
29
|
+
) -> list[Build]:
|
|
30
|
+
"""Get the list of builds
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
project (str): The name of the project
|
|
34
|
+
pipelines (list[int], optional): List of pipeline ids. Defaults to None.
|
|
35
|
+
start (datetime, optional): Earliest time. Defaults to None.
|
|
36
|
+
end (datetime, optional): Latest time. Defaults to None.
|
|
37
|
+
status (str, optional): The status to get. Defaults to None.
|
|
38
|
+
result (str, optional): The result to get. Defaults to None.
|
|
39
|
+
properties (list[str], optional): List or properties to return. Defaults to None.
|
|
40
|
+
branch (str, optional): The branch to search. Defaults to None.
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
list[Build]: List of all the builds that match the given criteria
|
|
44
|
+
"""
|
|
45
|
+
continuation = None
|
|
46
|
+
return [
|
|
47
|
+
Build(self.client, b)
|
|
48
|
+
for b in self.client.get_builds(
|
|
49
|
+
project,
|
|
50
|
+
continuation_token=continuation,
|
|
51
|
+
definitions=pipelines,
|
|
52
|
+
min_time=start,
|
|
53
|
+
max_time=end,
|
|
54
|
+
status_filter=status,
|
|
55
|
+
result_filter=result,
|
|
56
|
+
properties=properties,
|
|
57
|
+
branch_name=branch,
|
|
58
|
+
)
|
|
59
|
+
]
|
|
@@ -14,6 +14,7 @@ from msrest.authentication import BasicAuthentication as MSBasicAuthentication
|
|
|
14
14
|
from devopsdriver.settings import Settings
|
|
15
15
|
from devopsdriver.azdo.workitem.client import Client as WIClient
|
|
16
16
|
from devopsdriver.azdo.pipeline.client import Client as PLClient
|
|
17
|
+
from devopsdriver.azdo.builds.client import Client as BClient
|
|
17
18
|
|
|
18
19
|
|
|
19
20
|
# for testing
|
|
@@ -21,16 +22,16 @@ CONNECTION = AzureConnection
|
|
|
21
22
|
AUTHENTICATION = MSBasicAuthentication
|
|
22
23
|
|
|
23
24
|
|
|
24
|
-
class Azure: # pylint: disable=too-few-public-methods
|
|
25
|
+
class Azure: # pylint: disable=too-few-public-methods,too-many-instance-attributes
|
|
25
26
|
"""A connection to Azure clients"""
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
class _Client:
|
|
29
|
+
def __init__(self, client):
|
|
30
|
+
self.client = client
|
|
28
31
|
|
|
29
32
|
def __init__(
|
|
30
33
|
self, settings: Settings = None, token: str = None, url: str = None, **clients
|
|
31
34
|
):
|
|
32
|
-
unsupported_clients = set(clients) - Azure.SUPPORTED_CLIENTS
|
|
33
|
-
assert not unsupported_clients, f"{unsupported_clients} not supported"
|
|
34
35
|
settings = (
|
|
35
36
|
Settings(__file__).key("secrets")
|
|
36
37
|
if settings is None and token is None and url is None
|
|
@@ -47,9 +48,21 @@ class Azure: # pylint: disable=too-few-public-methods
|
|
|
47
48
|
client_calls = {
|
|
48
49
|
"workitem": self.connection.clients_v7_1.get_work_item_tracking_client,
|
|
49
50
|
"pipeline": self.connection.clients_v7_1.get_pipelines_client,
|
|
51
|
+
"task": self.connection.clients_v7_1.get_task_agent_client,
|
|
52
|
+
"git": self.connection.clients_v7_1.get_git_client,
|
|
53
|
+
"core": self.connection.clients_v7_1.get_core_client,
|
|
54
|
+
"build": self.connection.clients_v7_1.get_build_client,
|
|
55
|
+
"identity": self.connection.clients_v7_1.get_identity_client,
|
|
50
56
|
}
|
|
57
|
+
unsupported_clients = set(clients) - set(client_calls)
|
|
58
|
+
assert not unsupported_clients, f"{unsupported_clients} not supported"
|
|
51
59
|
self.workitem = WIClient(Azure.__client("workitem", clients, client_calls))
|
|
52
60
|
self.pipeline = PLClient(Azure.__client("pipeline", clients, client_calls))
|
|
61
|
+
self.core = Azure._Client(Azure.__client("core", clients, client_calls))
|
|
62
|
+
self.task = Azure._Client(Azure.__client("task", clients, client_calls))
|
|
63
|
+
self.git = Azure._Client(Azure.__client("git", clients, client_calls))
|
|
64
|
+
self.build = BClient(Azure.__client("build", clients, client_calls))
|
|
65
|
+
self.identity = Azure._Client(Azure.__client("identity", clients, client_calls))
|
|
53
66
|
|
|
54
67
|
@staticmethod
|
|
55
68
|
def __client(name: str, clients: dict, calls: dict) -> any:
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# pylint: disable=cyclic-import
|
|
2
|
+
|
|
3
|
+
# devopsdriver.azdo
|
|
4
|
+
# -> devopsdriver.azdo.clients
|
|
5
|
+
# -> devopsdriver.azdo.pipeline.client
|
|
6
|
+
# -> devopsdriver.azdo.pipeline.pipeline
|
|
7
|
+
|
|
8
|
+
# devopsdriver.azdo
|
|
9
|
+
# -> devopsdriver.azdo.clients
|
|
10
|
+
# -> devopsdriver.azdo.pipeline.client
|
|
11
|
+
# -> devopsdriver.azdo.pipeline.pipeline
|
|
12
|
+
# -> devopsdriver.azdo.pipeline.run
|
|
@@ -43,7 +43,7 @@ class Client:
|
|
|
43
43
|
top=top,
|
|
44
44
|
)
|
|
45
45
|
|
|
46
|
-
def
|
|
46
|
+
def get_history( # pylint: disable=too-many-arguments
|
|
47
47
|
self,
|
|
48
48
|
wi_id: int,
|
|
49
49
|
project: str = None,
|
|
@@ -86,5 +86,6 @@ class Client:
|
|
|
86
86
|
list[list[WorkItem]]: List of work items, each is a history of work items
|
|
87
87
|
"""
|
|
88
88
|
return [
|
|
89
|
-
[AzureObject(e) for e in self.
|
|
89
|
+
[AzureObject(e) for e in self.get_history(i)]
|
|
90
|
+
for i in self.find_ids(wiql, top)
|
|
90
91
|
]
|
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
""" Data Objects """
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
from json import dumps
|
|
7
|
+
|
|
8
|
+
|
|
6
9
|
class DataObject: # pylint: disable=too-few-public-methods
|
|
7
10
|
"""dict like object with fuzzy field matching"""
|
|
8
11
|
|
|
@@ -46,6 +49,12 @@ class DataObject: # pylint: disable=too-few-public-methods
|
|
|
46
49
|
def __getattr__(self, name: str) -> any:
|
|
47
50
|
return self._get_field(name, self.data)
|
|
48
51
|
|
|
52
|
+
def __str__(self) -> str:
|
|
53
|
+
return dumps(self.data, indent=2)
|
|
54
|
+
|
|
55
|
+
def __repr__(self) -> str:
|
|
56
|
+
return dumps(self.data, indent=2)
|
|
57
|
+
|
|
49
58
|
class _Dict(dict):
|
|
50
59
|
def __init__(self, dataobject, data: dict):
|
|
51
60
|
self.dataobject = dataobject
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: devopsdriver
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.45
|
|
4
4
|
Summary: DevOps tools
|
|
5
5
|
Author-email: Marc Page <marcallenpage@gmail.com>
|
|
6
6
|
License: This is free and unencumbered software released into the public domain.
|
|
@@ -40,7 +40,7 @@ Classifier: Intended Audience :: Developers
|
|
|
40
40
|
Classifier: Programming Language :: Python
|
|
41
41
|
Classifier: Programming Language :: Python :: 3
|
|
42
42
|
Classifier: License :: OSI Approved :: The Unlicense (Unlicense)
|
|
43
|
-
Classifier: Programming Language :: Python :: 3.
|
|
43
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
44
44
|
Classifier: Topic :: Utilities
|
|
45
45
|
Classifier: Topic :: Software Development
|
|
46
46
|
Requires-Python: >=3.10
|
|
@@ -48,9 +48,9 @@ Description-Content-Type: text/markdown
|
|
|
48
48
|
License-File: LICENSE
|
|
49
49
|
Requires-Dist: PyYAML==6.0.1
|
|
50
50
|
Requires-Dist: keyring==25.1.0
|
|
51
|
-
Requires-Dist: setuptools==69.
|
|
51
|
+
Requires-Dist: setuptools==69.5.1
|
|
52
52
|
Requires-Dist: azure-devops==7.1.0b4
|
|
53
|
-
Requires-Dist: Mako==1.3.
|
|
53
|
+
Requires-Dist: Mako==1.3.3
|
|
54
54
|
Provides-Extra: dev
|
|
55
55
|
Requires-Dist: black>=24.3.0; extra == "dev"
|
|
56
56
|
Requires-Dist: pylint>=3.1.0; extra == "dev"
|
|
@@ -60,7 +60,7 @@ Requires-Dist: coverage>=7.4.4; extra == "test"
|
|
|
60
60
|
Provides-Extra: doc
|
|
61
61
|
|
|
62
62
|

|
|
63
|
-
[](https://pypi.org/project/devopsdriver/0.1.45/)
|
|
64
64
|
[](https://github.com/marcpage/devops-driver?tab=Unlicense-1-ov-file#readme)
|
|
65
65
|
[](https://github.com/marcpage/devops-driver/graphs/contributors)
|
|
66
66
|
[](http://makeapullrequest.com)
|
|
@@ -17,6 +17,9 @@ devopsdriver/azdo/__init__.py
|
|
|
17
17
|
devopsdriver/azdo/azureobject.py
|
|
18
18
|
devopsdriver/azdo/clients.py
|
|
19
19
|
devopsdriver/azdo/timestamp.py
|
|
20
|
+
devopsdriver/azdo/builds/__init__.py
|
|
21
|
+
devopsdriver/azdo/builds/build.py
|
|
22
|
+
devopsdriver/azdo/builds/client.py
|
|
20
23
|
devopsdriver/azdo/pipeline/__init__.py
|
|
21
24
|
devopsdriver/azdo/pipeline/client.py
|
|
22
25
|
devopsdriver/azdo/pipeline/log.py
|
|
@@ -27,6 +30,8 @@ devopsdriver/azdo/workitem/client.py
|
|
|
27
30
|
devopsdriver/azdo/workitem/wiql.py
|
|
28
31
|
devopsdriver/templates/manage_settings.txt.mako
|
|
29
32
|
tests/test_azure_azureobject.py
|
|
33
|
+
tests/test_azure_build.py
|
|
34
|
+
tests/test_azure_build_client.py
|
|
30
35
|
tests/test_azure_clients.py
|
|
31
36
|
tests/test_azure_pipeline.py
|
|
32
37
|
tests/test_azure_pipeline_client.py
|
|
@@ -8,9 +8,9 @@ requires-python = ">= 3.10"
|
|
|
8
8
|
dependencies = [
|
|
9
9
|
"PyYAML==6.0.1",
|
|
10
10
|
"keyring==25.1.0",
|
|
11
|
-
"setuptools==69.
|
|
11
|
+
"setuptools==69.5.1", # neded for azure-devops to use 7.1 API
|
|
12
12
|
"azure-devops==7.1.0b4",
|
|
13
|
-
"Mako==1.3.
|
|
13
|
+
"Mako==1.3.3",
|
|
14
14
|
]
|
|
15
15
|
keywords = ["azure", "devops", "jira", "confluence", "email", "pipelines", "tools"]
|
|
16
16
|
classifiers=[
|
|
@@ -20,7 +20,7 @@ classifiers=[
|
|
|
20
20
|
"Programming Language :: Python",
|
|
21
21
|
"Programming Language :: Python :: 3",
|
|
22
22
|
"License :: OSI Approved :: The Unlicense (Unlicense)",
|
|
23
|
-
"Programming Language :: Python :: 3.
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
24
|
"Topic :: Utilities",
|
|
25
25
|
"Topic :: Software Development",
|
|
26
26
|
]
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
""" Test build object """
|
|
4
|
+
|
|
5
|
+
from types import SimpleNamespace
|
|
6
|
+
|
|
7
|
+
from devopsdriver.azdo.builds.build import Build
|
|
8
|
+
from devopsdriver.azdo.azureobject import AzureObject
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MockTimeline: # pylint: disable=too-few-public-methods
|
|
12
|
+
"""mock timeline"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, d: dict):
|
|
15
|
+
self.d = d
|
|
16
|
+
|
|
17
|
+
def as_dict(self) -> dict:
|
|
18
|
+
"""mock as_dict"""
|
|
19
|
+
return self.d
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MockClient:
|
|
23
|
+
"""mock build client"""
|
|
24
|
+
|
|
25
|
+
def get_build_log_lines(self, project, build_id, log_id) -> list[str]:
|
|
26
|
+
"""mock get_build_log_lines"""
|
|
27
|
+
return [f"project={project}", f"build={build_id}", f"log={log_id}"]
|
|
28
|
+
|
|
29
|
+
def get_build_timeline(self, project, build_id):
|
|
30
|
+
"""mock get_build_timeline"""
|
|
31
|
+
return SimpleNamespace(
|
|
32
|
+
records=[
|
|
33
|
+
MockTimeline(
|
|
34
|
+
{
|
|
35
|
+
"parent_id": None,
|
|
36
|
+
"id": "5",
|
|
37
|
+
"project": project,
|
|
38
|
+
"build_id": build_id,
|
|
39
|
+
"log": {"id": 9},
|
|
40
|
+
}
|
|
41
|
+
),
|
|
42
|
+
MockTimeline(
|
|
43
|
+
{
|
|
44
|
+
"parent_id": "5",
|
|
45
|
+
"id": "6",
|
|
46
|
+
"project": project,
|
|
47
|
+
"build_id": build_id,
|
|
48
|
+
"log": {"id": 6},
|
|
49
|
+
}
|
|
50
|
+
),
|
|
51
|
+
]
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class MockBuild(AzureObject): # pylint: disable=too-few-public-methods
|
|
56
|
+
"""mock build client"""
|
|
57
|
+
|
|
58
|
+
def __init__(self, d: dict):
|
|
59
|
+
self.d = d
|
|
60
|
+
super().__init__(self)
|
|
61
|
+
|
|
62
|
+
def as_dict(self) -> dict:
|
|
63
|
+
"""mock as_dict"""
|
|
64
|
+
return self.d
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def test_basic() -> None:
|
|
68
|
+
"""test basic functionailty"""
|
|
69
|
+
build = Build(MockClient(), MockBuild({"project": {"name": "project"}, "id": 12}))
|
|
70
|
+
logs = build.get_logs().all_logs()
|
|
71
|
+
assert len(logs) == 2, logs
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_azure_object() -> None:
|
|
75
|
+
"""test string conversions for data objects"""
|
|
76
|
+
build = MockBuild({"project": {"name": "project"}, "id": 12})
|
|
77
|
+
assert (
|
|
78
|
+
str(build)
|
|
79
|
+
== """{
|
|
80
|
+
"project": {
|
|
81
|
+
"name": "project"
|
|
82
|
+
},
|
|
83
|
+
"id": 12
|
|
84
|
+
}"""
|
|
85
|
+
), str(build)
|
|
86
|
+
assert (
|
|
87
|
+
repr(build)
|
|
88
|
+
== """{
|
|
89
|
+
"project": {
|
|
90
|
+
"name": "project"
|
|
91
|
+
},
|
|
92
|
+
"id": 12
|
|
93
|
+
}"""
|
|
94
|
+
), repr(build)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
if __name__ == "__main__":
|
|
98
|
+
test_basic()
|
|
99
|
+
test_azure_object()
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
""" Test azure build client """
|
|
4
|
+
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
|
|
7
|
+
from devopsdriver.azdo.builds.client import Client
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class MockObject:
|
|
11
|
+
"""mock object"""
|
|
12
|
+
|
|
13
|
+
def __init__(self, d: dict):
|
|
14
|
+
self.d = d
|
|
15
|
+
|
|
16
|
+
def as_dict(self) -> dict:
|
|
17
|
+
"""mock as_dict
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
dict: the dict
|
|
21
|
+
"""
|
|
22
|
+
return self.d
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class MockClient:
|
|
26
|
+
"""mock build client"""
|
|
27
|
+
|
|
28
|
+
def get_builds(
|
|
29
|
+
self,
|
|
30
|
+
project,
|
|
31
|
+
definitions,
|
|
32
|
+
min_time,
|
|
33
|
+
max_time,
|
|
34
|
+
status_filter,
|
|
35
|
+
result_filter,
|
|
36
|
+
properties,
|
|
37
|
+
branch_name,
|
|
38
|
+
continuation_token=None,
|
|
39
|
+
):
|
|
40
|
+
"""mock get_builds"""
|
|
41
|
+
return [
|
|
42
|
+
MockObject(
|
|
43
|
+
{
|
|
44
|
+
"project": project,
|
|
45
|
+
"pipelines": definitions,
|
|
46
|
+
"start": min_time,
|
|
47
|
+
"end": max_time,
|
|
48
|
+
"status": status_filter,
|
|
49
|
+
"result": result_filter,
|
|
50
|
+
"fields": properties,
|
|
51
|
+
"branch": branch_name,
|
|
52
|
+
"token": continuation_token,
|
|
53
|
+
}
|
|
54
|
+
)
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_basic() -> None:
|
|
59
|
+
"""Test basic functionality"""
|
|
60
|
+
client = Client(MockClient())
|
|
61
|
+
results = client.list(
|
|
62
|
+
"project",
|
|
63
|
+
[1, 2, 3],
|
|
64
|
+
datetime.now(),
|
|
65
|
+
datetime.now(),
|
|
66
|
+
"completed",
|
|
67
|
+
"success",
|
|
68
|
+
["id"],
|
|
69
|
+
"main",
|
|
70
|
+
)
|
|
71
|
+
assert len(results) == 1, results
|
|
72
|
+
assert results[0].project == "project"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
if __name__ == "__main__":
|
|
76
|
+
test_basic()
|
|
@@ -24,13 +24,23 @@ class MockConnection: # pylint: disable=too-few-public-methods
|
|
|
24
24
|
class Clients71: # pylint: disable=too-few-public-methods
|
|
25
25
|
"""Fakes a 7.1 clients factory"""
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
""
|
|
29
|
-
|
|
27
|
+
SUPPORTED = {
|
|
28
|
+
"get_work_item_tracking_client",
|
|
29
|
+
"get_pipelines_client",
|
|
30
|
+
"get_task_agent_client",
|
|
31
|
+
"get_git_client",
|
|
32
|
+
"get_core_client",
|
|
33
|
+
"get_build_client",
|
|
34
|
+
"get_identity_client",
|
|
35
|
+
}
|
|
30
36
|
|
|
31
|
-
def
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
def __getattr__(self, name: str) -> str:
|
|
38
|
+
assert name in Clients71.SUPPORTED, name
|
|
39
|
+
|
|
40
|
+
def function():
|
|
41
|
+
return name.replace("get_", "")
|
|
42
|
+
|
|
43
|
+
return function
|
|
34
44
|
|
|
35
45
|
self.clients_v7_1 = Clients71()
|
|
36
46
|
|
|
@@ -124,7 +134,9 @@ def test_load_settings() -> None:
|
|
|
124
134
|
), azure.connection.base_url
|
|
125
135
|
assert azure.connection.creds.token == "token", azure.connection.creds.token
|
|
126
136
|
assert azure.connection.creds.empty == "", azure.connection.creds.empty
|
|
127
|
-
assert
|
|
137
|
+
assert (
|
|
138
|
+
azure.workitem.client == "work_item_tracking_client"
|
|
139
|
+
), azure.workitem.client
|
|
128
140
|
|
|
129
141
|
|
|
130
142
|
def test_not_all_clients() -> None:
|
|
@@ -140,7 +152,20 @@ def test_not_all_clients() -> None:
|
|
|
140
152
|
assert azure.workitem.client is None
|
|
141
153
|
|
|
142
154
|
|
|
155
|
+
def test_unsupported_client() -> None:
|
|
156
|
+
"""test the basic calling"""
|
|
157
|
+
clients.CONNECTION = MockConnection
|
|
158
|
+
clients.AUTHENTICATION = lambda a, b: SimpleNamespace(empty=a, token=b)
|
|
159
|
+
try:
|
|
160
|
+
_ = Azure(None, "token", "https://url.com/project", google=True)
|
|
161
|
+
raise KeyError("google should not have worked")
|
|
162
|
+
|
|
163
|
+
except AssertionError as error:
|
|
164
|
+
assert "google" in str(error), error
|
|
165
|
+
|
|
166
|
+
|
|
143
167
|
if __name__ == "__main__":
|
|
168
|
+
test_unsupported_client()
|
|
144
169
|
test_not_all_clients()
|
|
145
170
|
test_load_settings()
|
|
146
171
|
test_mixed_settings()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|