artefacts-cli 0.9.3__tar.gz → 0.9.5__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.
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/CHANGELOG.md +18 -3
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/PKG-INFO +5 -1
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/__init__.py +31 -36
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/app.py +98 -43
- artefacts_cli-0.9.5/artefacts/cli/config.py +210 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/helpers.py +12 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/locales/art.pot +97 -64
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/locales/base.pot +97 -64
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/locales/click.pot +2 -2
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/locales/ja_JP/LC_MESSAGES/artefacts.po +101 -63
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/logger.py +5 -1
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/ros2.py +1 -1
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/version.py +2 -2
- artefacts_cli-0.9.5/artefacts/copava/__init__.py +1 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts_cli.egg-info/PKG-INFO +5 -1
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts_cli.egg-info/SOURCES.txt +2 -3
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts_cli.egg-info/requires.txt +5 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/bin/release +5 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/pyproject.toml +6 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/pytest.ini +4 -0
- artefacts_cli-0.9.5/scripts/whats_latest.rb +11 -0
- artefacts_cli-0.9.5/tests/cli/test_cli.py +219 -0
- artefacts_cli-0.9.5/tests/cli/test_config_validation.py +24 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/cli/test_utils.py +1 -1
- artefacts_cli-0.9.5/tests/cli/test_warp.py +287 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/cli/test_warp_run.py +79 -18
- artefacts_cli-0.9.5/tests/conftest.py +195 -0
- artefacts_cli-0.9.3/tests/fixtures/warp.yaml → artefacts_cli-0.9.5/tests/fixtures/artefacts.yaml.template +1 -1
- artefacts_cli-0.9.3/artefacts/cli/config.py +0 -62
- artefacts_cli-0.9.3/artefacts/copava/__init__.py +0 -1
- artefacts_cli-0.9.3/tests/cli/test_cli.py +0 -144
- artefacts_cli-0.9.3/tests/cli/test_config_validation.py +0 -70
- artefacts_cli-0.9.3/tests/cli/test_warp.py +0 -117
- artefacts_cli-0.9.3/tests/conftest.py +0 -93
- artefacts_cli-0.9.3/tests/fixtures/artefacts_ros1.yaml +0 -32
- artefacts_cli-0.9.3/tests/test_config_validation.py +0 -70
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/.pyre_configuration +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/README.md +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/README_INTERNAL.md +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/__init__.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/app_containers.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/app_containers.pyi +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/bagparser.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/constants.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/containers/__init__.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/containers/docker_cm.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/containers/docker_utils.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/containers/utils.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/errors.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/i18n.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/other.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/parameters.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/ros1.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/utils.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/cli/utils_ros.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts/wrappers/artefacts_ros1_meta.launch +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts.yaml +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts_cli.egg-info/dependency_links.txt +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts_cli.egg-info/entry_points.txt +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/artefacts_cli.egg-info/top_level.txt +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/infra-tests/test_run_remote.yaml +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/CMakeLists.txt +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/launch/test_meta.launch +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/launch/test_turtle.launch +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/launch/turtle_odometry.launch +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/package.xml +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/setup.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/src/TestTurtle.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/src/__init__.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/src/turtle_odom.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/src/turtle_post_process.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/src/turtle_trajectory.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/infra-tests/turtlesim1/ros_workspace/src/turtle_odometry/test/viz_turtle_odom.xml +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/infra-tests/turtlesim2/launch_turtle.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/infra-tests/turtlesim2/sample_node.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/setup.cfg +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/__init__.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/cli/__init__.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/cli/containers/__init__.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/cli/containers/test_docker.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/cli/containers/test_utils.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/cli/test_app_containers.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/cli/test_other.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/cli/test_parameters.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/cli/test_ros1.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/cli/test_ros2.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/cli/test_utils_ros.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/fixtures/artefacts_deprecated.yaml +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/fixtures/bad_launch_test.py +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/fixtures/test_junit.xml +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/fixtures/warp-env-param.yaml +0 -0
- {artefacts_cli-0.9.3 → artefacts_cli-0.9.5}/tests/utils/docker_mock.py +0 -0
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
7
7
|
|
8
8
|
## [Unreleased]
|
9
9
|
|
10
|
+
TODO and upcoming
|
11
|
+
|
12
|
+
## [0.9.5] - 2025-06-30
|
13
|
+
|
14
|
+
### Added
|
15
|
+
|
16
|
+
- API calls are automatically retried up to 3 times on server-side 502, 503, 504 errors (#264)
|
17
|
+
- API calls get short timeout to detect network issues and report ot the user nicely. (#264)
|
18
|
+
|
19
|
+
### Fixed
|
20
|
+
|
21
|
+
- Wrong error handling on `artefacts config add`, resulting in HTML `artefacts.yaml` (#220)
|
22
|
+
- Correct number of runs in a given job now correctly submitted to the dashboard api (#281)
|
23
|
+
- Jobs moves onto next run on certain errors which previously terminated the job in run local (#281)
|
24
|
+
|
10
25
|
## [0.9.3] - 2025-06-03
|
11
26
|
|
12
27
|
### Removed
|
@@ -105,9 +120,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
105
120
|
- Local metrics errors do not block publication of results.
|
106
121
|
- Introduction of Black formatting.
|
107
122
|
|
108
|
-
[unreleased]: https://github.com/art-e-fact/artefacts-client/compare/0.9.
|
109
|
-
[0.9.
|
110
|
-
[0.9.
|
123
|
+
[unreleased]: https://github.com/art-e-fact/artefacts-client/compare/0.9.5...HEAD
|
124
|
+
[0.9.5]: https://github.com/art-e-fact/artefacts-client/releases/tag/0.9.5
|
125
|
+
[0.9.3]: https://github.com/art-e-fact/artefacts-client/releases/tag/0.9.3
|
111
126
|
[0.8.0]: https://github.com/art-e-fact/artefacts-client/releases/tag/0.8.0
|
112
127
|
[0.7.0]: https://github.com/art-e-fact/artefacts-client/releases/tag/0.7.0
|
113
128
|
[0.5.8]: https://github.com/art-e-fact/artefacts-client/releases/tag/0.5.8
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: artefacts_cli
|
3
|
-
Version: 0.9.
|
3
|
+
Version: 0.9.5
|
4
4
|
Author-email: FD <fabian@artefacts.com>, AGC <alejandro@artefacts.com>, TN <tomo@artefacts.com>, EP <eric@artefacts.com>
|
5
5
|
License-Expression: Apache-2.0
|
6
6
|
Project-URL: Homepage, https://github.com/art-e-fact/artefacts-client
|
@@ -20,6 +20,7 @@ Requires-Dist: mcap-ros2-support
|
|
20
20
|
Requires-Dist: PyYAML>=6.0
|
21
21
|
Requires-Dist: requests>=2.27.1
|
22
22
|
Requires-Dist: rospkg
|
23
|
+
Requires-Dist: urllib3>=2.4.0
|
23
24
|
Provides-Extra: dev
|
24
25
|
Requires-Dist: awscli; extra == "dev"
|
25
26
|
Requires-Dist: babel==2.17.0; extra == "dev"
|
@@ -41,6 +42,9 @@ Requires-Dist: python-markdown-math; extra == "dev"
|
|
41
42
|
Requires-Dist: ruff>=0.9.2; extra == "dev"
|
42
43
|
Requires-Dist: setuptools_scm>=8; extra == "dev"
|
43
44
|
Requires-Dist: twine; extra == "dev"
|
45
|
+
Provides-Extra: dev-extended
|
46
|
+
Requires-Dist: artefacts_cli[dev]; extra == "dev-extended"
|
47
|
+
Requires-Dist: pytest-watch; extra == "dev-extended"
|
44
48
|
|
45
49
|
# Artefacts CLI
|
46
50
|
|
@@ -6,7 +6,6 @@ import glob
|
|
6
6
|
import json
|
7
7
|
import math
|
8
8
|
import os
|
9
|
-
import requests
|
10
9
|
|
11
10
|
from .config import APIConf
|
12
11
|
from .i18n import localise
|
@@ -33,7 +32,15 @@ except PackageNotFoundError:
|
|
33
32
|
__version__ = "0.0.0"
|
34
33
|
|
35
34
|
|
36
|
-
class
|
35
|
+
class ArtefactsAPIError(Exception):
|
36
|
+
"""
|
37
|
+
Tentative base error class for Artefacts API interactions
|
38
|
+
"""
|
39
|
+
|
40
|
+
pass
|
41
|
+
|
42
|
+
|
43
|
+
class AuthenticationError(ArtefactsAPIError):
|
37
44
|
"""Raised when artefacts authentication failed"""
|
38
45
|
|
39
46
|
pass
|
@@ -52,6 +59,7 @@ class WarpJob:
|
|
52
59
|
noisolation=False,
|
53
60
|
context=None,
|
54
61
|
run_offset=0,
|
62
|
+
n_subjobs=1, # Total Number of Runs
|
55
63
|
):
|
56
64
|
self.project_id = project_id
|
57
65
|
self.job_id = os.environ.get("ARTEFACTS_JOB_ID", None)
|
@@ -67,28 +75,27 @@ class WarpJob:
|
|
67
75
|
self.noupload = noupload
|
68
76
|
self.noisolation = noisolation
|
69
77
|
self.context = context
|
78
|
+
self.n_subjobs = n_subjobs
|
70
79
|
|
71
80
|
if dryrun:
|
72
81
|
self.job_id = "dryrun"
|
73
82
|
if self.job_id is None:
|
74
83
|
# Only create a new job if job_id is not specified
|
75
84
|
data = {
|
85
|
+
"project_id": self.project_id,
|
76
86
|
"start": round(self.start),
|
77
87
|
"status": "in progress",
|
78
88
|
"params": json.dumps(self.params),
|
79
89
|
"project": self.project_id,
|
80
90
|
"jobname": self.jobname,
|
81
91
|
"timeout": self.params.get("timeout", 5) * 60,
|
92
|
+
"n_subjobs": self.n_subjobs,
|
82
93
|
}
|
83
94
|
if context is not None:
|
84
95
|
data["message"] = context["description"]
|
85
96
|
data["commit"] = context["commit"]
|
86
97
|
data["ref"] = context["ref"]
|
87
|
-
response =
|
88
|
-
f"{api_conf.api_url}/{self.project_id}/job",
|
89
|
-
json=data,
|
90
|
-
headers=api_conf.headers,
|
91
|
-
)
|
98
|
+
response = self.api_conf.create("job", data)
|
92
99
|
if response.status_code != 200:
|
93
100
|
if response.status_code == 403:
|
94
101
|
msg = response.json()["message"]
|
@@ -102,7 +109,7 @@ class WarpJob:
|
|
102
109
|
)
|
103
110
|
)
|
104
111
|
logger.warning(response.text)
|
105
|
-
raise
|
112
|
+
raise ArtefactsAPIError(str(response.status_code))
|
106
113
|
self.job_id = response.json()["job_id"]
|
107
114
|
self.output_path = self.params.get("output_path", f"/tmp/{self.job_id}")
|
108
115
|
os.makedirs(self.output_path, exist_ok=True)
|
@@ -111,24 +118,21 @@ class WarpJob:
|
|
111
118
|
def log_tests_result(self, success):
|
112
119
|
self.success = success
|
113
120
|
|
114
|
-
def
|
121
|
+
def update(self, last_run_success: bool) -> bool:
|
115
122
|
end = datetime.now(timezone.utc).timestamp()
|
116
123
|
if self.dryrun:
|
117
|
-
return
|
124
|
+
return True
|
118
125
|
# Log metadata
|
119
126
|
data = {
|
127
|
+
"project_id": self.project_id,
|
120
128
|
"end": round(end),
|
121
129
|
"duration": round(end - self.start),
|
122
|
-
"success":
|
130
|
+
"success": last_run_success,
|
123
131
|
"status": "finished", # need to be determined based on all runs
|
124
132
|
}
|
125
|
-
|
126
|
-
f"{self.api_conf.api_url}/{self.project_id}/job/{self.job_id}",
|
127
|
-
json=data,
|
128
|
-
headers=self.api_conf.headers,
|
129
|
-
)
|
133
|
+
response = self.api_conf.update("job", self.job_id, data)
|
130
134
|
|
131
|
-
return
|
135
|
+
return response.status_code == 200
|
132
136
|
|
133
137
|
def new_run(self, scenario):
|
134
138
|
run = WarpRun(self, scenario["name"], scenario, self.n_runs)
|
@@ -153,6 +157,7 @@ class WarpRun:
|
|
153
157
|
self.logger = logger
|
154
158
|
os.makedirs(self.output_path, exist_ok=True)
|
155
159
|
data = {
|
160
|
+
"project_id": self.job.project_id,
|
156
161
|
"job_id": job.job_id,
|
157
162
|
"run_n": self.run_n,
|
158
163
|
"start": round(self.start),
|
@@ -163,14 +168,8 @@ class WarpRun:
|
|
163
168
|
|
164
169
|
if self.job.dryrun:
|
165
170
|
return
|
166
|
-
|
167
|
-
|
168
|
-
)
|
169
|
-
response = requests.post(
|
170
|
-
query_url,
|
171
|
-
json=data,
|
172
|
-
headers=self.job.api_conf.headers,
|
173
|
-
)
|
171
|
+
|
172
|
+
response = self.job.api_conf.create("run", data)
|
174
173
|
if response.status_code != 200:
|
175
174
|
if response.status_code == 403:
|
176
175
|
msg = response.json()["message"]
|
@@ -184,7 +183,7 @@ class WarpRun:
|
|
184
183
|
)
|
185
184
|
)
|
186
185
|
self.logger.warning(response.text)
|
187
|
-
raise
|
186
|
+
raise ArtefactsAPIError(str(response.status_code))
|
188
187
|
return
|
189
188
|
|
190
189
|
def log_params(self, params):
|
@@ -266,6 +265,7 @@ class WarpRun:
|
|
266
265
|
|
267
266
|
# Log metadata
|
268
267
|
data = {
|
268
|
+
"project_id": self.job.project_id,
|
269
269
|
"job_id": self.job.job_id,
|
270
270
|
"run_n": self.run_n,
|
271
271
|
"start": math.floor(self.start),
|
@@ -280,11 +280,7 @@ class WarpRun:
|
|
280
280
|
if not self.job.noupload:
|
281
281
|
data["uploads"] = self.uploads
|
282
282
|
|
283
|
-
response =
|
284
|
-
f"{self.job.api_conf.api_url}/{self.job.project_id}/job/{self.job.job_id}/run/{self.run_n}",
|
285
|
-
json=data,
|
286
|
-
headers=self.job.api_conf.headers,
|
287
|
-
)
|
283
|
+
response = self.job.api_conf.update("run", self.run_n, data)
|
288
284
|
|
289
285
|
# use s3 presigned urls to upload the artifacts
|
290
286
|
if self.job.noupload:
|
@@ -307,11 +303,8 @@ class WarpRun:
|
|
307
303
|
)
|
308
304
|
)
|
309
305
|
)
|
310
|
-
|
311
|
-
|
312
|
-
upload_info["url"],
|
313
|
-
data=upload_info["fields"],
|
314
|
-
files=files,
|
306
|
+
self.job.api_conf.upload(
|
307
|
+
upload_info["url"], upload_info["fields"], files
|
315
308
|
)
|
316
309
|
except OverflowError:
|
317
310
|
self.logger.warning(
|
@@ -342,6 +335,7 @@ def init_job(
|
|
342
335
|
noisolation: bool = False,
|
343
336
|
context: Optional[dict] = None,
|
344
337
|
run_offset=0,
|
338
|
+
n_subjobs: int = 1,
|
345
339
|
):
|
346
340
|
return WarpJob(
|
347
341
|
project_id,
|
@@ -354,6 +348,7 @@ def init_job(
|
|
354
348
|
noisolation,
|
355
349
|
context,
|
356
350
|
run_offset,
|
351
|
+
n_subjobs,
|
357
352
|
)
|
358
353
|
|
359
354
|
|
@@ -20,6 +20,7 @@ from artefacts.cli import (
|
|
20
20
|
init_job,
|
21
21
|
generate_scenarios,
|
22
22
|
localise,
|
23
|
+
logger,
|
23
24
|
AuthenticationError,
|
24
25
|
__version__,
|
25
26
|
)
|
@@ -32,6 +33,7 @@ from artefacts.cli.constants import (
|
|
32
33
|
)
|
33
34
|
from artefacts.cli.helpers import (
|
34
35
|
add_key_to_conf,
|
36
|
+
endpoint_exists,
|
35
37
|
get_conf_from_file,
|
36
38
|
get_artefacts_api_url,
|
37
39
|
get_git_revision_branch,
|
@@ -64,39 +66,70 @@ def add(project_name):
|
|
64
66
|
profile = config[project_name]
|
65
67
|
else:
|
66
68
|
profile = {}
|
69
|
+
|
67
70
|
api_url = get_artefacts_api_url(profile)
|
68
71
|
dashboard_url = api_url.split("/api")[0]
|
72
|
+
|
69
73
|
settings_page_url = f"{dashboard_url}/{project_name}/settings"
|
70
|
-
# Check if running on WSL
|
71
|
-
if "WSLENV" in os.environ:
|
72
|
-
os.system(f'cmd.exe /C start "" {settings_page_url} 2>/dev/null')
|
73
|
-
else:
|
74
|
-
webbrowser.open(settings_page_url)
|
75
74
|
click.echo(
|
76
75
|
localise("Opening the project settings page: {url}").format(
|
77
76
|
url=settings_page_url
|
78
77
|
)
|
79
78
|
)
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
79
|
+
|
80
|
+
if endpoint_exists(settings_page_url):
|
81
|
+
# Check if running on WSL
|
82
|
+
if "WSLENV" in os.environ:
|
83
|
+
os.system(f'cmd.exe /C start "" {settings_page_url} 2>/dev/null')
|
84
|
+
else:
|
85
|
+
webbrowser.open(settings_page_url)
|
86
|
+
|
87
|
+
api_key = click.prompt(
|
88
|
+
localise("Please enter your API KEY for {project}").format(
|
89
|
+
project=project_name
|
90
|
+
),
|
91
|
+
type=str,
|
92
|
+
hide_input=True,
|
93
|
+
)
|
94
|
+
add_key_to_conf(project_name, api_key)
|
95
|
+
click.echo(localise("API KEY saved for {project}").format(project=project_name))
|
96
|
+
if click.confirm(
|
97
|
+
localise(
|
98
|
+
"Would you like to download a pregenerated artefacts.yaml file? This will overwrite any existing config file in the current directory."
|
99
|
+
)
|
100
|
+
):
|
101
|
+
config_file_name = "artefacts.yaml"
|
102
|
+
config_file_url = f"{api_url}/{project_name}/{config_file_name}"
|
103
|
+
# Get credentials only now, as API key set just before.
|
104
|
+
api_conf = APIConf(project_name, __version__)
|
105
|
+
config_response = api_conf.read("url", config_file_url)
|
106
|
+
if config_response.status_code == 200:
|
107
|
+
with open(config_file_name, "wb") as f:
|
108
|
+
f.write(config_response.content)
|
109
|
+
else:
|
110
|
+
click.echo(
|
111
|
+
localise(
|
112
|
+
"We encountered a problem in getting the generated configuration file. Please consider downloading it from the project page on the dashboard at {url}. Sorry for the inconvenience."
|
113
|
+
).format(url=settings_page_url)
|
114
|
+
)
|
115
|
+
logger.debug(
|
116
|
+
localise(
|
117
|
+
"If you are using an alternative server, please also consider checking the value of ARTEFACTS_API_URL in your environment."
|
118
|
+
)
|
119
|
+
)
|
120
|
+
else:
|
121
|
+
click.echo(
|
122
|
+
localise(
|
123
|
+
"Our apologies: The project page does not seem available at the moment. If `{project_name}` is correct, please try again later.".format(
|
124
|
+
project_name=project_name
|
125
|
+
)
|
126
|
+
)
|
127
|
+
)
|
128
|
+
click.echo(
|
129
|
+
localise(
|
130
|
+
"If you are using an alternative server, please also consider checking the value of ARTEFACTS_API_URL in your environment."
|
131
|
+
)
|
92
132
|
)
|
93
|
-
):
|
94
|
-
api_conf = APIConf(project_name, __version__)
|
95
|
-
config_file_name = "artefacts.yaml"
|
96
|
-
config_file_url = f"{api_url}/{project_name}/{config_file_name}"
|
97
|
-
r = requests.get(config_file_url, headers=api_conf.headers)
|
98
|
-
with open(config_file_name, "wb") as f:
|
99
|
-
f.write(r.content)
|
100
133
|
return
|
101
134
|
|
102
135
|
|
@@ -118,10 +151,7 @@ def delete(project_name):
|
|
118
151
|
def hello(project_name):
|
119
152
|
"""Show message to confirm credentials allow access to PROJECT_NAME"""
|
120
153
|
api_conf = APIConf(project_name, __version__)
|
121
|
-
response =
|
122
|
-
f"{api_conf.api_url}/{project_name}/info",
|
123
|
-
headers=api_conf.headers,
|
124
|
-
)
|
154
|
+
response = api_conf.read("url", f"{api_conf.api_url}/{project_name}/info")
|
125
155
|
if response.status_code == 200:
|
126
156
|
result = response.json()
|
127
157
|
click.echo(
|
@@ -360,6 +390,7 @@ def run(
|
|
360
390
|
noisolation,
|
361
391
|
context,
|
362
392
|
first,
|
393
|
+
len(scenarios),
|
363
394
|
)
|
364
395
|
except AuthenticationError:
|
365
396
|
click.secho(
|
@@ -412,6 +443,8 @@ def run(
|
|
412
443
|
)
|
413
444
|
run.log_tests_results([result], False)
|
414
445
|
run.stop()
|
446
|
+
warpjob.update(last_run_success=False)
|
447
|
+
continue
|
415
448
|
if dryrun:
|
416
449
|
click.echo(f"[{jobname}] " + localise("Performing dry run"))
|
417
450
|
results, success = {}, True
|
@@ -419,7 +452,8 @@ def run(
|
|
419
452
|
try:
|
420
453
|
results, success = run_ros2_tests(run)
|
421
454
|
except Exception as e:
|
422
|
-
|
455
|
+
run.stop()
|
456
|
+
warpjob.update(last_run_success=False)
|
423
457
|
warpjob.log_tests_result(False)
|
424
458
|
click.secho(e, bold=True, err=True)
|
425
459
|
click.secho(
|
@@ -428,10 +462,18 @@ def run(
|
|
428
462
|
err=True,
|
429
463
|
bold=True,
|
430
464
|
)
|
431
|
-
|
465
|
+
continue
|
432
466
|
if success is None:
|
467
|
+
result = get_TestSuite_error_result(
|
468
|
+
scenario["name"],
|
469
|
+
localise("ROS2 environment error"),
|
470
|
+
localise(
|
471
|
+
"Not able to execute tests. Make sure that ROS2 is sourced and that your launch file syntax is correct."
|
472
|
+
),
|
473
|
+
)
|
474
|
+
run.log_tests_results([result], False)
|
433
475
|
run.stop()
|
434
|
-
warpjob.
|
476
|
+
warpjob.update(last_run_success=False)
|
435
477
|
warpjob.log_tests_result(job_success)
|
436
478
|
click.secho(
|
437
479
|
f"[{jobname}] "
|
@@ -441,7 +483,7 @@ def run(
|
|
441
483
|
err=True,
|
442
484
|
bold=True,
|
443
485
|
)
|
444
|
-
|
486
|
+
continue
|
445
487
|
if not success:
|
446
488
|
job_success = False
|
447
489
|
elif framework is not None and framework.startswith("ros1:"):
|
@@ -454,7 +496,17 @@ def run(
|
|
454
496
|
err=True,
|
455
497
|
bold=True,
|
456
498
|
)
|
457
|
-
|
499
|
+
result = get_TestSuite_error_result(
|
500
|
+
scenario["name"],
|
501
|
+
localise("launch_test file not specified error"),
|
502
|
+
localise(
|
503
|
+
"Please specify a `ros_testfile` in the artefacts.yaml scenario configuration."
|
504
|
+
),
|
505
|
+
)
|
506
|
+
run.log_tests_results([result], False)
|
507
|
+
run.stop()
|
508
|
+
warpjob.update(last_run_success=False)
|
509
|
+
continue
|
458
510
|
if dryrun:
|
459
511
|
click.echo(f"[{jobname}] " + localise("Performing dry run"))
|
460
512
|
results, success = {}, True
|
@@ -472,7 +524,10 @@ def run(
|
|
472
524
|
err=True,
|
473
525
|
bold=True,
|
474
526
|
)
|
475
|
-
|
527
|
+
run.stop()
|
528
|
+
warpjob.update(last_run_success=False)
|
529
|
+
warpjob.log_tests_result(False)
|
530
|
+
continue
|
476
531
|
if dryrun:
|
477
532
|
click.echo(f"[{jobname}] " + localise("Performing dry run"))
|
478
533
|
results, success = {}, True
|
@@ -487,12 +542,11 @@ def run(
|
|
487
542
|
add_output_from_default(run)
|
488
543
|
|
489
544
|
run.stop()
|
490
|
-
|
545
|
+
warpjob.log_tests_result(job_success)
|
546
|
+
warpjob.update(last_run_success=run.success)
|
491
547
|
click.echo(f"[{jobname}] " + localise("Done"))
|
492
548
|
time.sleep(random.random() * 1)
|
493
549
|
|
494
|
-
warpjob.stop()
|
495
|
-
|
496
550
|
|
497
551
|
@click.command()
|
498
552
|
@click.option(
|
@@ -590,9 +644,10 @@ def run_remote(config, description, jobname, skip_validation=False):
|
|
590
644
|
temp_file.seek(0)
|
591
645
|
|
592
646
|
# Request signed upload URLs
|
593
|
-
|
594
|
-
|
595
|
-
|
647
|
+
# TODO Horrible approach, but the endpoint is neither CRUD nor REST, etc.
|
648
|
+
# So the idea to remain horrible and change this soon.
|
649
|
+
upload_urls_response = api_conf.direct("put")(
|
650
|
+
f"{api_conf.api_url}/{project_id}/upload_source"
|
596
651
|
)
|
597
652
|
|
598
653
|
if not upload_urls_response.ok:
|
@@ -694,10 +749,10 @@ def run_remote(config, description, jobname, skip_validation=False):
|
|
694
749
|
item_show_func=lambda x: x and x[0],
|
695
750
|
) as bar:
|
696
751
|
for filename, file in bar:
|
697
|
-
response =
|
752
|
+
response = api_conf.upload(
|
698
753
|
upload_urls[filename]["url"],
|
699
|
-
|
700
|
-
|
754
|
+
upload_urls[filename]["fields"],
|
755
|
+
{"file": file},
|
701
756
|
)
|
702
757
|
if not response.ok:
|
703
758
|
raise click.ClickException(
|