devopsdriver 0.1.41__tar.gz → 0.1.43__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.41 → devopsdriver-0.1.43}/PKG-INFO +2 -2
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/README.md +1 -1
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver/__init__.py +1 -1
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver/azdo/__init__.py +1 -1
- devopsdriver-0.1.43/devopsdriver/azdo/azureobject.py +32 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver/azdo/clients.py +4 -1
- devopsdriver-0.1.43/devopsdriver/azdo/pipeline/__init__.py +0 -0
- devopsdriver-0.1.43/devopsdriver/azdo/pipeline/client.py +30 -0
- devopsdriver-0.1.43/devopsdriver/azdo/pipeline/log.py +28 -0
- devopsdriver-0.1.43/devopsdriver/azdo/pipeline/pipeline.py +35 -0
- devopsdriver-0.1.43/devopsdriver/azdo/pipeline/run.py +45 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver/azdo/workitem/client.py +3 -3
- devopsdriver-0.1.43/devopsdriver/dataobject.py +55 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver.egg-info/PKG-INFO +2 -2
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver.egg-info/SOURCES.txt +12 -2
- devopsdriver-0.1.41/tests/test_azure_workitem.py → devopsdriver-0.1.43/tests/test_azure_azureobject.py +4 -4
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/tests/test_azure_clients.py +4 -0
- devopsdriver-0.1.43/tests/test_azure_pipeline.py +47 -0
- devopsdriver-0.1.43/tests/test_azure_pipeline_client.py +35 -0
- devopsdriver-0.1.43/tests/test_azure_pipeline_run.py +68 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/tests/test_azure_timestamp.py +1 -0
- devopsdriver-0.1.43/tests/test_dataobject.py +29 -0
- devopsdriver-0.1.41/devopsdriver/azdo/workitem/workitem.py +0 -68
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/LICENSE +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver/azdo/timestamp.py +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver/azdo/workitem/__init__.py +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver/azdo/workitem/wiql.py +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver/manage_settings.py +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver/sendmail.py +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver/settings.py +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver/template.py +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver/templates/manage_settings.txt.mako +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver.egg-info/dependency_links.txt +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver.egg-info/entry_points.txt +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver.egg-info/requires.txt +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/devopsdriver.egg-info/top_level.txt +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/pyproject.toml +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/setup.cfg +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/tests/test_azure_workitem_client.py +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/tests/test_azure_workitem_wiql.py +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/tests/test_manage_settings.py +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/tests/test_sendmail.py +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/tests/test_settings.py +0 -0
- {devopsdriver-0.1.41 → devopsdriver-0.1.43}/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.43
|
|
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.
|
|
@@ -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.43/)
|
|
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.43/)
|
|
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)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
""" initialize azure module """
|
|
2
2
|
|
|
3
3
|
# re export symbols for easier use
|
|
4
|
+
from .azureobject import AzureObject
|
|
4
5
|
from .clients import Azure
|
|
5
6
|
from .timestamp import Timestamp
|
|
6
7
|
|
|
7
|
-
from .workitem.workitem import WorkItem
|
|
8
8
|
from .workitem.wiql import Wiql, Value, Field
|
|
9
9
|
from .workitem.wiql import Ascending, Descending, And, Or, In, NotIn
|
|
10
10
|
from .workitem.wiql import Equal, NotEqual, LessThanOrEqual, GreaterThanOrEqual
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
""" An Azure Devops WorkItem """
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from msrest.serialization import Model
|
|
8
|
+
|
|
9
|
+
from devopsdriver.azdo.timestamp import Timestamp
|
|
10
|
+
from devopsdriver.dataobject import DataObject
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AzureObject(DataObject): # pylint: disable=too-few-public-methods
|
|
14
|
+
"""Azure WorkItem"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, azure_object: Model):
|
|
17
|
+
self.raw = azure_object
|
|
18
|
+
super().__init__(self.raw.as_dict())
|
|
19
|
+
|
|
20
|
+
def _parse_value(self, data: any) -> any:
|
|
21
|
+
if isinstance(data, str) and Timestamp.is_timestamp(data):
|
|
22
|
+
return Timestamp(data)
|
|
23
|
+
|
|
24
|
+
return super()._parse_value(data)
|
|
25
|
+
|
|
26
|
+
def _get_field(self, name: str, data: dict) -> any:
|
|
27
|
+
value = super()._get_field(name, data)
|
|
28
|
+
|
|
29
|
+
if value is None and "fields" in data:
|
|
30
|
+
return self._get_field(name, data["fields"])
|
|
31
|
+
|
|
32
|
+
return value
|
|
@@ -13,6 +13,7 @@ from msrest.authentication import BasicAuthentication as MSBasicAuthentication
|
|
|
13
13
|
|
|
14
14
|
from devopsdriver.settings import Settings
|
|
15
15
|
from devopsdriver.azdo.workitem.client import Client as WIClient
|
|
16
|
+
from devopsdriver.azdo.pipeline.client import Client as PLClient
|
|
16
17
|
|
|
17
18
|
|
|
18
19
|
# for testing
|
|
@@ -44,9 +45,11 @@ class Azure: # pylint: disable=too-few-public-methods
|
|
|
44
45
|
token = settings["azure.token"] if token is None else token
|
|
45
46
|
self.connection = CONNECTION(base_url=url, creds=AUTHENTICATION("", token))
|
|
46
47
|
client_calls = {
|
|
47
|
-
"workitem": self.connection.clients_v7_1.get_work_item_tracking_client
|
|
48
|
+
"workitem": self.connection.clients_v7_1.get_work_item_tracking_client,
|
|
49
|
+
"pipeline": self.connection.clients_v7_1.get_pipelines_client,
|
|
48
50
|
}
|
|
49
51
|
self.workitem = WIClient(Azure.__client("workitem", clients, client_calls))
|
|
52
|
+
self.pipeline = PLClient(Azure.__client("pipeline", clients, client_calls))
|
|
50
53
|
|
|
51
54
|
@staticmethod
|
|
52
55
|
def __client(name: str, clients: dict, calls: dict) -> any:
|
|
File without changes
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
""" Pipeline client """
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from azure.devops.v7_1.pipelines import PipelinesClient
|
|
8
|
+
|
|
9
|
+
from .pipeline import Pipeline
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Client: # pylint: disable=too-few-public-methods
|
|
13
|
+
"""Pipeline Client wrapper"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, client: PipelinesClient):
|
|
16
|
+
self.client = client
|
|
17
|
+
|
|
18
|
+
def list(self, project: str) -> list[Pipeline]:
|
|
19
|
+
"""List the pipelines
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
project (str): The project to list
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
list[Pipeline]: The list of pipeline objects
|
|
26
|
+
"""
|
|
27
|
+
return [
|
|
28
|
+
Pipeline(self.client, project, p)
|
|
29
|
+
for p in self.client.list_pipelines(project)
|
|
30
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
""" Azure Pipeline Run Log """
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from azure.devops.v7_1.pipelines.models import Log as AzureLog
|
|
8
|
+
|
|
9
|
+
from requests import get as get_url
|
|
10
|
+
|
|
11
|
+
from devopsdriver.azdo.azureobject import AzureObject
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
GET_URL = get_url
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class Log(AzureObject): # pylint: disable=too-few-public-methods
|
|
18
|
+
"""Log fields:
|
|
19
|
+
created_on ("2024-04-08T23:00:59.020Z")
|
|
20
|
+
id (8)
|
|
21
|
+
last_changed_on ("2024-04-08T23:00:59.160Z")
|
|
22
|
+
line_count (4)
|
|
23
|
+
url ("https://dev.azure.com/Org/<guid>/_apis/pipelines/1/runs/10/logs/8")
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, log: AzureLog):
|
|
27
|
+
self.text = GET_URL(log.signed_content.url, timeout=1).text
|
|
28
|
+
super().__init__(log)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
""" Azure Pipeline """
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from azure.devops.v7_1.pipelines.models import Pipeline as AzurePipeline
|
|
8
|
+
from azure.devops.v7_1.pipelines import PipelinesClient
|
|
9
|
+
|
|
10
|
+
from devopsdriver.azdo import AzureObject
|
|
11
|
+
|
|
12
|
+
from .run import Run
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Pipeline(AzureObject): # pylint: disable=too-few-public-methods
|
|
16
|
+
"""Pipeline fields:
|
|
17
|
+
folder ("\\")
|
|
18
|
+
id (1)
|
|
19
|
+
name (DevOps)
|
|
20
|
+
revision (1),
|
|
21
|
+
_links ({}),
|
|
22
|
+
url (https://dev.azure.com/Org/<guid>/_apis/pipelines/1?revision=1)
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, client: PipelinesClient, project: str, pipeline: AzurePipeline):
|
|
26
|
+
self.client = client
|
|
27
|
+
self.project = project
|
|
28
|
+
super().__init__(pipeline)
|
|
29
|
+
|
|
30
|
+
def get_runs(self):
|
|
31
|
+
"""Gets the top 10,000 runs for this pipeline"""
|
|
32
|
+
return [
|
|
33
|
+
Run(self.client, self.project, self.raw, r)
|
|
34
|
+
for r in self.client.list_runs(self.project, self.id)
|
|
35
|
+
]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
""" Pipeline Run """
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from azure.devops.v7_1.pipelines.models import Pipeline
|
|
8
|
+
from azure.devops.v7_1.pipelines.models import Run as AzureRun
|
|
9
|
+
from azure.devops.v7_1.pipelines import PipelinesClient
|
|
10
|
+
|
|
11
|
+
from devopsdriver.azdo import AzureObject
|
|
12
|
+
|
|
13
|
+
from .log import Log
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Run(AzureObject): # pylint: disable=too-few-public-methods
|
|
17
|
+
"""Run fields:
|
|
18
|
+
id (1)
|
|
19
|
+
name ("20240325.1")
|
|
20
|
+
_links ({})
|
|
21
|
+
created_date ("2024-03-25T23:53:38.503126Z")
|
|
22
|
+
finished_date ("2024-03-25T23:53:43.467565Z")
|
|
23
|
+
pipeline (see Pipeline)
|
|
24
|
+
result ("failed")
|
|
25
|
+
state ("completed")
|
|
26
|
+
template_parameters ({})
|
|
27
|
+
url ("https://dev.azure.com/Org/<guid>/_apis/pipelines/1/runs/1")
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(
|
|
31
|
+
self, client: PipelinesClient, project: str, pipeline: Pipeline, run: AzureRun
|
|
32
|
+
):
|
|
33
|
+
self.client = client
|
|
34
|
+
self.project = project
|
|
35
|
+
self.pipeline = pipeline
|
|
36
|
+
super().__init__(run)
|
|
37
|
+
|
|
38
|
+
def logs(self):
|
|
39
|
+
"""Get Logs for the run"""
|
|
40
|
+
return [
|
|
41
|
+
Log(l)
|
|
42
|
+
for l in self.client.list_logs(
|
|
43
|
+
self.project, self.pipeline.id, self.raw.id, expand="signedContent"
|
|
44
|
+
).logs
|
|
45
|
+
]
|
|
@@ -8,7 +8,7 @@ from azure.devops.v7_1.work_item_tracking.models import Wiql as AzureWiql
|
|
|
8
8
|
from azure.devops.v7_1.work_item_tracking.models import WorkItem as AzureWorkItem
|
|
9
9
|
from azure.devops.v7_1.work_item_tracking.models import TeamContext
|
|
10
10
|
from azure.devops.v7_1.work_item_tracking.models import WorkItemQueryResult
|
|
11
|
-
from devopsdriver.azdo.
|
|
11
|
+
from devopsdriver.azdo.azureobject import AzureObject
|
|
12
12
|
from devopsdriver.azdo.workitem.wiql import Wiql
|
|
13
13
|
|
|
14
14
|
|
|
@@ -75,7 +75,7 @@ class Client:
|
|
|
75
75
|
# columns: list of name, reference_name, url
|
|
76
76
|
return [i.id for i in found.work_items]
|
|
77
77
|
|
|
78
|
-
def find(self, wiql: Wiql | str, top: int = None) -> list[list[
|
|
78
|
+
def find(self, wiql: Wiql | str, top: int = None) -> list[list[AzureObject]]:
|
|
79
79
|
"""Gets the full history of items found in a WIQL search
|
|
80
80
|
|
|
81
81
|
Args:
|
|
@@ -86,5 +86,5 @@ class Client:
|
|
|
86
86
|
list[list[WorkItem]]: List of work items, each is a history of work items
|
|
87
87
|
"""
|
|
88
88
|
return [
|
|
89
|
-
[
|
|
89
|
+
[AzureObject(e) for e in self.history(i)] for i in self.find_ids(wiql, top)
|
|
90
90
|
]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
""" Data Objects """
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DataObject: # pylint: disable=too-few-public-methods
|
|
7
|
+
"""dict like object with fuzzy field matching"""
|
|
8
|
+
|
|
9
|
+
def __init__(self, data: dict):
|
|
10
|
+
self.data = data
|
|
11
|
+
|
|
12
|
+
def _matches_field(self, name: str, field: str) -> bool:
|
|
13
|
+
name = name.lower()
|
|
14
|
+
field = field.lower()
|
|
15
|
+
|
|
16
|
+
if name == field:
|
|
17
|
+
return True
|
|
18
|
+
|
|
19
|
+
if name == field.replace(".", "_"):
|
|
20
|
+
return True
|
|
21
|
+
|
|
22
|
+
if name == field.split(".")[-1]:
|
|
23
|
+
return True
|
|
24
|
+
|
|
25
|
+
return False
|
|
26
|
+
|
|
27
|
+
def _parse_value(self, data: any) -> any:
|
|
28
|
+
if isinstance(data, dict):
|
|
29
|
+
return DataObject._Dict(self, data)
|
|
30
|
+
|
|
31
|
+
if isinstance(data, list):
|
|
32
|
+
return [self._parse_value(d) for d in data]
|
|
33
|
+
|
|
34
|
+
return data
|
|
35
|
+
|
|
36
|
+
def _get_field(self, name: str, data: dict) -> any:
|
|
37
|
+
assert name and data, f"name = {name} data = {data}"
|
|
38
|
+
found = [f for f in data if self._matches_field(name, f)]
|
|
39
|
+
assert len(found) in {0, 1}, found
|
|
40
|
+
|
|
41
|
+
if len(found) == 1:
|
|
42
|
+
return self._parse_value(data[found[0]])
|
|
43
|
+
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
def __getattr__(self, name: str) -> any:
|
|
47
|
+
return self._get_field(name, self.data)
|
|
48
|
+
|
|
49
|
+
class _Dict(dict):
|
|
50
|
+
def __init__(self, dataobject, data: dict):
|
|
51
|
+
self.dataobject = dataobject
|
|
52
|
+
super().__init__(data)
|
|
53
|
+
|
|
54
|
+
def __getattr__(self, name: str) -> any:
|
|
55
|
+
return self.dataobject._get_field(name, self)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: devopsdriver
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.43
|
|
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.
|
|
@@ -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.43/)
|
|
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)
|
|
@@ -2,6 +2,7 @@ LICENSE
|
|
|
2
2
|
README.md
|
|
3
3
|
pyproject.toml
|
|
4
4
|
devopsdriver/__init__.py
|
|
5
|
+
devopsdriver/dataobject.py
|
|
5
6
|
devopsdriver/manage_settings.py
|
|
6
7
|
devopsdriver/sendmail.py
|
|
7
8
|
devopsdriver/settings.py
|
|
@@ -13,18 +14,27 @@ devopsdriver.egg-info/entry_points.txt
|
|
|
13
14
|
devopsdriver.egg-info/requires.txt
|
|
14
15
|
devopsdriver.egg-info/top_level.txt
|
|
15
16
|
devopsdriver/azdo/__init__.py
|
|
17
|
+
devopsdriver/azdo/azureobject.py
|
|
16
18
|
devopsdriver/azdo/clients.py
|
|
17
19
|
devopsdriver/azdo/timestamp.py
|
|
20
|
+
devopsdriver/azdo/pipeline/__init__.py
|
|
21
|
+
devopsdriver/azdo/pipeline/client.py
|
|
22
|
+
devopsdriver/azdo/pipeline/log.py
|
|
23
|
+
devopsdriver/azdo/pipeline/pipeline.py
|
|
24
|
+
devopsdriver/azdo/pipeline/run.py
|
|
18
25
|
devopsdriver/azdo/workitem/__init__.py
|
|
19
26
|
devopsdriver/azdo/workitem/client.py
|
|
20
27
|
devopsdriver/azdo/workitem/wiql.py
|
|
21
|
-
devopsdriver/azdo/workitem/workitem.py
|
|
22
28
|
devopsdriver/templates/manage_settings.txt.mako
|
|
29
|
+
tests/test_azure_azureobject.py
|
|
23
30
|
tests/test_azure_clients.py
|
|
31
|
+
tests/test_azure_pipeline.py
|
|
32
|
+
tests/test_azure_pipeline_client.py
|
|
33
|
+
tests/test_azure_pipeline_run.py
|
|
24
34
|
tests/test_azure_timestamp.py
|
|
25
|
-
tests/test_azure_workitem.py
|
|
26
35
|
tests/test_azure_workitem_client.py
|
|
27
36
|
tests/test_azure_workitem_wiql.py
|
|
37
|
+
tests/test_dataobject.py
|
|
28
38
|
tests/test_manage_settings.py
|
|
29
39
|
tests/test_sendmail.py
|
|
30
40
|
tests/test_settings.py
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
""" Test work item """
|
|
4
4
|
|
|
5
|
-
from devopsdriver.azdo import
|
|
5
|
+
from devopsdriver.azdo import AzureObject
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class MockAzureWorkItem:
|
|
8
|
+
class MockAzureWorkItem: # pylint: disable=too-few-public-methods
|
|
9
9
|
"""mock out work item"""
|
|
10
10
|
|
|
11
11
|
def as_dict(self):
|
|
@@ -76,7 +76,7 @@ class MockAzureWorkItem:
|
|
|
76
76
|
|
|
77
77
|
def test_workitem() -> None:
|
|
78
78
|
"""test basic work item"""
|
|
79
|
-
wi =
|
|
79
|
+
wi = AzureObject(MockAzureWorkItem())
|
|
80
80
|
assert wi.id == 5, wi.id
|
|
81
81
|
assert wi.ID == 5, wi.ID
|
|
82
82
|
assert wi.workitemtype == "User Story", wi.workitemtype
|
|
@@ -87,7 +87,7 @@ def test_workitem() -> None:
|
|
|
87
87
|
|
|
88
88
|
def test_timestamp() -> None:
|
|
89
89
|
"""test timestamps"""
|
|
90
|
-
wi =
|
|
90
|
+
wi = AzureObject(MockAzureWorkItem())
|
|
91
91
|
assert wi.StateChangeDate.to_string() == "2023-11-16T03:12:32.94Z"
|
|
92
92
|
assert wi.CreatedDate.to_string() == "2023-11-16T03:12:32.94Z"
|
|
93
93
|
assert wi.ChangedDate.to_string() == "2023-11-16T03:12:32.94Z"
|
|
@@ -28,6 +28,10 @@ class MockConnection: # pylint: disable=too-few-public-methods
|
|
|
28
28
|
"""fakes getting work item client"""
|
|
29
29
|
return "work_item_tracking_client"
|
|
30
30
|
|
|
31
|
+
def get_pipelines_client(self) -> str:
|
|
32
|
+
"""mocks getting pipeline client"""
|
|
33
|
+
return "get_pipelines_client"
|
|
34
|
+
|
|
31
35
|
self.clients_v7_1 = Clients71()
|
|
32
36
|
|
|
33
37
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
""" Test Pipeline """
|
|
4
|
+
|
|
5
|
+
from devopsdriver.azdo.pipeline.pipeline import Pipeline
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MockRun: # pylint: disable=too-few-public-methods
|
|
9
|
+
"""moc out a run"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, value: str):
|
|
12
|
+
self.value = value
|
|
13
|
+
|
|
14
|
+
def as_dict(self):
|
|
15
|
+
"""mock out list_runs method"""
|
|
16
|
+
return {"id": self.value}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MockPipelineClient:
|
|
20
|
+
"""mock pipeline client"""
|
|
21
|
+
|
|
22
|
+
def as_dict(self):
|
|
23
|
+
"""mock out as_dict method"""
|
|
24
|
+
return {}
|
|
25
|
+
|
|
26
|
+
def list_runs(self, project: str, pipeline_id: int):
|
|
27
|
+
"""mock out list_runs method"""
|
|
28
|
+
return [MockRun(project), MockRun(pipeline_id)]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class MockPipeline: # pylint: disable=too-few-public-methods
|
|
32
|
+
"""mock out Azure pipeline object"""
|
|
33
|
+
|
|
34
|
+
def as_dict(self):
|
|
35
|
+
"""mock out list_runs method"""
|
|
36
|
+
return {"id": 5}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_pipeline() -> None:
|
|
40
|
+
"""tests basic pipeline functionality"""
|
|
41
|
+
pipeline = Pipeline(MockPipelineClient(), "project", MockPipeline())
|
|
42
|
+
runs = pipeline.get_runs()
|
|
43
|
+
assert len(runs) == 2, runs
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
if __name__ == "__main__":
|
|
47
|
+
test_pipeline()
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
""" Test Pipeline Client"""
|
|
4
|
+
|
|
5
|
+
from devopsdriver.azdo.pipeline.client import Client
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class MockAzurePipeline: # pylint: disable=too-few-public-methods
|
|
9
|
+
"""mock azure pipeline object"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, project: str):
|
|
12
|
+
self.project = project
|
|
13
|
+
|
|
14
|
+
def as_dict(self):
|
|
15
|
+
"""mock as_dict"""
|
|
16
|
+
return {"project": self.project}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MockAzurePipelinesClient: # pylint: disable=too-few-public-methods
|
|
20
|
+
"""mock pipelines client"""
|
|
21
|
+
|
|
22
|
+
def list_pipelines(self, project: str):
|
|
23
|
+
"""mock list_pipelines"""
|
|
24
|
+
return [MockAzurePipeline(f"{project} 1"), MockAzurePipeline(f"{project} 2")]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_client() -> None:
|
|
28
|
+
"""tests the pipeline client"""
|
|
29
|
+
client = Client(MockAzurePipelinesClient())
|
|
30
|
+
pipelines = client.list("project")
|
|
31
|
+
assert len(pipelines) == 2, pipelines
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
test_client()
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
""" Test pipeline run """
|
|
4
|
+
|
|
5
|
+
from types import SimpleNamespace
|
|
6
|
+
|
|
7
|
+
from devopsdriver.azdo.pipeline.run import Run
|
|
8
|
+
from devopsdriver.azdo.pipeline import log
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MockAzureRun: # pylint: disable=too-few-public-methods
|
|
12
|
+
"""Mock azure run object"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, run_id):
|
|
15
|
+
self.id = run_id
|
|
16
|
+
|
|
17
|
+
def as_dict(self):
|
|
18
|
+
"""mock as_dict"""
|
|
19
|
+
return {"id": 48}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class MockLog: # pylint: disable=too-few-public-methods
|
|
23
|
+
"""mock azure log object"""
|
|
24
|
+
|
|
25
|
+
def __init__(self, text):
|
|
26
|
+
self.text = text
|
|
27
|
+
self.signed_content = SimpleNamespace(url=SimpleNamespace(text="some dumb url"))
|
|
28
|
+
|
|
29
|
+
def as_dict(self):
|
|
30
|
+
"""mock as_dict"""
|
|
31
|
+
return {"id": 22}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class MockPipelineClient: # pylint: disable=too-few-public-methods
|
|
35
|
+
"""mock azure pipelines client"""
|
|
36
|
+
|
|
37
|
+
def __init__(self):
|
|
38
|
+
self.project = None
|
|
39
|
+
self.pipeline_id = None
|
|
40
|
+
self.run_id = None
|
|
41
|
+
self.expand = None
|
|
42
|
+
|
|
43
|
+
def list_logs(self, project, pipeline_id, run_id, expand):
|
|
44
|
+
"""mock list_logs"""
|
|
45
|
+
self.project = project
|
|
46
|
+
self.pipeline_id = pipeline_id
|
|
47
|
+
self.run_id = run_id
|
|
48
|
+
self.expand = expand
|
|
49
|
+
return SimpleNamespace(logs=[MockLog("log 1"), MockLog("log 2")])
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class MockPipeline: # pylint: disable=too-few-public-methods
|
|
53
|
+
"""mock pipeline object"""
|
|
54
|
+
|
|
55
|
+
def __init__(self, pipeline_id):
|
|
56
|
+
self.id = pipeline_id
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_basic() -> None:
|
|
60
|
+
"""test basic run and log"""
|
|
61
|
+
log.GET_URL = lambda x, timeout: x
|
|
62
|
+
run = Run(MockPipelineClient(), "project", MockPipeline(5), MockAzureRun(83))
|
|
63
|
+
logs = run.logs()
|
|
64
|
+
assert len(logs) == 2, logs
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
if __name__ == "__main__":
|
|
68
|
+
test_basic()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
""" Testing dataobject """
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from devopsdriver.dataobject import DataObject
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def test_dataobject_basic() -> None:
|
|
10
|
+
"""tests the basic dataobject"""
|
|
11
|
+
data = DataObject(
|
|
12
|
+
{
|
|
13
|
+
"Test": 5,
|
|
14
|
+
"system.Go": 12,
|
|
15
|
+
"fields": {"harry": 16, "people": [{"name": "john"}, 5, "test"]},
|
|
16
|
+
}
|
|
17
|
+
)
|
|
18
|
+
assert data.test == 5, data.test
|
|
19
|
+
assert data.go == 12, data.go
|
|
20
|
+
assert data.system_go == 12, data.system_go
|
|
21
|
+
assert data.fields.Harry == 16, data.fields.Harry
|
|
22
|
+
assert data.Harry is None, data.Harry
|
|
23
|
+
assert data.fields.people[0].Name == "john", data.fields.people[0].Name
|
|
24
|
+
assert data.fields.people[1] == 5, data.fields.people[1]
|
|
25
|
+
assert data.fields.people[2] == "test", data.fields.people[2]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
if __name__ == "__main__":
|
|
29
|
+
test_dataobject_basic()
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
""" An Azure Devops WorkItem """
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
from typing import Any
|
|
8
|
-
|
|
9
|
-
from azure.devops.v7_1.work_item_tracking.models import WorkItem as AzureWorkItem
|
|
10
|
-
|
|
11
|
-
from devopsdriver.azdo.timestamp import Timestamp
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class WorkItem: # pylint: disable=too-few-public-methods
|
|
15
|
-
"""Azure WorkItem"""
|
|
16
|
-
|
|
17
|
-
def __init__(self, work_item: AzureWorkItem):
|
|
18
|
-
self.raw = work_item
|
|
19
|
-
|
|
20
|
-
@staticmethod
|
|
21
|
-
def __matches_field(name: str, field: str) -> bool:
|
|
22
|
-
name = name.lower()
|
|
23
|
-
field = field.lower()
|
|
24
|
-
|
|
25
|
-
if name == field:
|
|
26
|
-
return True
|
|
27
|
-
|
|
28
|
-
if name == field.replace(".", "_"):
|
|
29
|
-
return True
|
|
30
|
-
|
|
31
|
-
if name == field.split(".")[-1]:
|
|
32
|
-
return True
|
|
33
|
-
|
|
34
|
-
return False
|
|
35
|
-
|
|
36
|
-
@staticmethod
|
|
37
|
-
def _parse_field(name: str, data: dict) -> any:
|
|
38
|
-
assert name and data
|
|
39
|
-
found = [f for f in data if WorkItem.__matches_field(name, f)]
|
|
40
|
-
|
|
41
|
-
if len(found) == 1:
|
|
42
|
-
return (
|
|
43
|
-
WorkItem._Dict(data[found[0]])
|
|
44
|
-
if isinstance(data[found[0]], dict)
|
|
45
|
-
else data[found[0]]
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
if "fields" in data:
|
|
49
|
-
return WorkItem._parse_field(name, data["fields"])
|
|
50
|
-
|
|
51
|
-
return None
|
|
52
|
-
|
|
53
|
-
class _Dict(dict):
|
|
54
|
-
def __getattr__(self, name: str) -> Any:
|
|
55
|
-
value = WorkItem._parse_field(name, self)
|
|
56
|
-
|
|
57
|
-
if Timestamp.is_timestamp(value):
|
|
58
|
-
return Timestamp(value)
|
|
59
|
-
|
|
60
|
-
return value
|
|
61
|
-
|
|
62
|
-
def __getattr__(self, name: str) -> Any:
|
|
63
|
-
value = WorkItem._parse_field(name, self.raw.as_dict())
|
|
64
|
-
|
|
65
|
-
if Timestamp.is_timestamp(value):
|
|
66
|
-
return Timestamp(value)
|
|
67
|
-
|
|
68
|
-
return value
|
|
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
|