primitive 0.1.59__py3-none-any.whl → 0.1.60__py3-none-any.whl
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.
- primitive/__about__.py +1 -1
- primitive/agent/actions.py +1 -0
- primitive/agent/process.py +2 -2
- primitive/agent/provision.py +2 -8
- primitive/agent/runner.py +18 -10
- primitive/agent/uploader.py +78 -49
- primitive/utils/cache.py +14 -0
- primitive/utils/shell.py +25 -0
- {primitive-0.1.59.dist-info → primitive-0.1.60.dist-info}/METADATA +1 -1
- {primitive-0.1.59.dist-info → primitive-0.1.60.dist-info}/RECORD +13 -13
- {primitive-0.1.59.dist-info → primitive-0.1.60.dist-info}/WHEEL +0 -0
- {primitive-0.1.59.dist-info → primitive-0.1.60.dist-info}/entry_points.txt +0 -0
- {primitive-0.1.59.dist-info → primitive-0.1.60.dist-info}/licenses/LICENSE.txt +0 -0
primitive/__about__.py
CHANGED
primitive/agent/actions.py
CHANGED
primitive/agent/process.py
CHANGED
@@ -34,7 +34,7 @@ class Process:
|
|
34
34
|
self.sel.register(self.process.stdout, selectors.EVENT_READ)
|
35
35
|
self.sel.register(self.process.stderr, selectors.EVENT_READ)
|
36
36
|
|
37
|
-
def
|
37
|
+
def log(self):
|
38
38
|
for key, _ in self.sel.select():
|
39
39
|
data = key.fileobj.readline()
|
40
40
|
if not data:
|
@@ -48,7 +48,7 @@ class Process:
|
|
48
48
|
|
49
49
|
def wait(self):
|
50
50
|
while True:
|
51
|
-
self.
|
51
|
+
self.log()
|
52
52
|
if not self.is_running():
|
53
53
|
break
|
54
54
|
|
primitive/agent/provision.py
CHANGED
@@ -2,6 +2,7 @@ import sys
|
|
2
2
|
from subprocess import Popen, PIPE
|
3
3
|
from pathlib import Path
|
4
4
|
from typing import Dict
|
5
|
+
from ..utils.shell import env_string_to_dict
|
5
6
|
|
6
7
|
|
7
8
|
class ProvisionPython:
|
@@ -34,14 +35,7 @@ class ProvisionPython:
|
|
34
35
|
output, _ = proc.communicate()
|
35
36
|
|
36
37
|
# Split the output into lines and parse it into a dictionary
|
37
|
-
env_vars =
|
38
|
-
|
39
|
-
for line in output.splitlines():
|
40
|
-
var_line = line.split("=", 1)
|
41
|
-
|
42
|
-
if len(var_line) == 2:
|
43
|
-
key, value = var_line
|
44
|
-
env_vars[key] = value
|
38
|
+
env_vars = env_string_to_dict(output)
|
45
39
|
|
46
40
|
cmd = f"python -m pip install -r {self.requirements_path}"
|
47
41
|
proc = Popen(
|
primitive/agent/runner.py
CHANGED
@@ -7,11 +7,10 @@ from typing import Dict, Iterable, List, Optional, TypedDict
|
|
7
7
|
|
8
8
|
import yaml
|
9
9
|
from loguru import logger
|
10
|
-
|
11
|
-
from ..utils.cache import get_artifacts_cache
|
12
|
-
from ..utils.files import find_files_for_extension
|
13
10
|
from .process import Process
|
14
11
|
from .provision import ProvisionPython
|
12
|
+
from ..utils.cache import get_artifacts_cache, get_logs_cache
|
13
|
+
from ..utils.files import find_files_for_extension
|
15
14
|
|
16
15
|
try:
|
17
16
|
from yaml import CLoader as Loader
|
@@ -56,12 +55,11 @@ class AgentRunner:
|
|
56
55
|
self.job_slug = job_slug
|
57
56
|
self.max_log_size = max_log_size
|
58
57
|
self.artifacts_dir = get_artifacts_cache(self.job_id)
|
58
|
+
self.logs_dir = get_logs_cache(self.job_id)
|
59
|
+
self.logger_handle = None
|
59
60
|
|
60
61
|
logger.enable("primitive")
|
61
|
-
self.
|
62
|
-
Path(self.artifacts_dir / "runner_{time}.log"),
|
63
|
-
rotation=self.max_log_size, # Rotate when the log file reaches 10MB
|
64
|
-
)
|
62
|
+
self.swap_logs(label="init")
|
65
63
|
|
66
64
|
logger.info(f"Scanning directory for job {self.job_slug}")
|
67
65
|
|
@@ -113,6 +111,9 @@ class AgentRunner:
|
|
113
111
|
conclusion = None
|
114
112
|
total_errors = 0
|
115
113
|
for step in self.steps():
|
114
|
+
# Swap logger
|
115
|
+
self.swap_logs(label=step["name"])
|
116
|
+
|
116
117
|
logger.info(f"Beginning step {step['name']}")
|
117
118
|
|
118
119
|
# Update workdir
|
@@ -138,7 +139,7 @@ class AgentRunner:
|
|
138
139
|
while proc.is_running():
|
139
140
|
# Check job status
|
140
141
|
status = self.primitive.jobs.get_job_status(self.job_id)
|
141
|
-
status_value = status["jobRun"]["status"]
|
142
|
+
status_value = status.data["jobRun"]["status"]
|
142
143
|
|
143
144
|
# TODO: Should probably use request_cancelled or something
|
144
145
|
# once we change it, we'll have to call conclude w/ cancelled status
|
@@ -184,6 +185,15 @@ class AgentRunner:
|
|
184
185
|
logger.info(f"Completed {self.job_slug} job")
|
185
186
|
logger.remove(self.logger_handle)
|
186
187
|
|
188
|
+
def swap_logs(self, label: str):
|
189
|
+
# Remove Handle
|
190
|
+
if self.logger_handle:
|
191
|
+
logger.remove(self.logger_handle)
|
192
|
+
|
193
|
+
self.logger_handle = logger.add(
|
194
|
+
Path(self.logs_dir / f"{label}_{{time}}.primitive.log"), rotation=self.max_log_size
|
195
|
+
)
|
196
|
+
|
187
197
|
def provision(self) -> Optional[Dict]:
|
188
198
|
match self.job["provision"]:
|
189
199
|
case "python":
|
@@ -199,8 +209,6 @@ class AgentRunner:
|
|
199
209
|
return prov.create_env()
|
200
210
|
|
201
211
|
def collect_artifacts(self, step: JobStep) -> None:
|
202
|
-
# str(PurePath(file_path).relative_to(Path(source))
|
203
|
-
|
204
212
|
# Search each artifact type
|
205
213
|
for artifact in step["artifacts"]:
|
206
214
|
files = find_files_for_extension(self.source_dir, artifact["extension"])
|
primitive/agent/uploader.py
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
import typing
|
2
|
+
from typing import Dict
|
2
3
|
import shutil
|
3
4
|
import os
|
4
5
|
from loguru import logger
|
5
6
|
from pathlib import Path, PurePath
|
6
|
-
from ..utils.cache import get_artifacts_cache
|
7
|
+
from ..utils.cache import get_artifacts_cache, get_logs_cache
|
7
8
|
|
8
9
|
if typing.TYPE_CHECKING:
|
9
10
|
import primitive.client
|
@@ -21,57 +22,85 @@ class Uploader:
|
|
21
22
|
path, key_prefix=prefix, job_run_id=job_run_id
|
22
23
|
)
|
23
24
|
return file_upload_response.json()["data"]["fileUpload"]["id"]
|
25
|
+
|
26
|
+
def upload_dir(self, cache: Path) -> Dict:
|
27
|
+
file_ids = []
|
28
|
+
job_run_id = cache.name
|
29
|
+
|
30
|
+
files = None
|
31
|
+
has_walk = getattr(cache, "walk", None)
|
32
|
+
if has_walk:
|
33
|
+
files = sorted(
|
34
|
+
[
|
35
|
+
current_path / file
|
36
|
+
for current_path, _, current_path_files in cache.walk()
|
37
|
+
for file in current_path_files
|
38
|
+
],
|
39
|
+
key=lambda p: p.stat().st_size,
|
40
|
+
)
|
41
|
+
else:
|
42
|
+
files = sorted(
|
43
|
+
[
|
44
|
+
Path(Path(current_path) / file)
|
45
|
+
for current_path, _, current_path_files in os.walk(cache)
|
46
|
+
for file in current_path_files
|
47
|
+
],
|
48
|
+
key=lambda p: p.stat().st_size,
|
49
|
+
)
|
50
|
+
|
51
|
+
for file in files:
|
52
|
+
upload_id = self.upload_file(
|
53
|
+
file,
|
54
|
+
prefix=str(PurePath(file).relative_to(cache.parent).parent),
|
55
|
+
job_run_id=job_run_id,
|
56
|
+
)
|
57
|
+
|
58
|
+
if upload_id:
|
59
|
+
file_ids.append(upload_id)
|
60
|
+
continue
|
61
|
+
|
62
|
+
logger.error(f"Unable to upload file {file}")
|
63
|
+
|
64
|
+
# Clean up job cache
|
65
|
+
shutil.rmtree(path=cache)
|
66
|
+
|
67
|
+
return {job_run_id: file_ids}
|
68
|
+
|
24
69
|
|
25
70
|
def scan(self) -> None:
|
26
71
|
# Scan artifacts directory
|
27
72
|
artifacts_dir = get_artifacts_cache()
|
73
|
+
logs_dir = get_logs_cache()
|
28
74
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
75
|
+
artifacts = sorted([
|
76
|
+
artifacts_cache
|
77
|
+
for artifacts_cache in artifacts_dir.iterdir()
|
78
|
+
if artifacts_cache.is_dir()
|
79
|
+
], key=lambda p: p.stat().st_ctime)
|
80
|
+
|
81
|
+
logs = sorted([
|
82
|
+
logs_cache
|
83
|
+
for logs_cache in logs_dir.iterdir()
|
84
|
+
if logs_cache.is_dir()
|
85
|
+
], key=lambda p: p.stat().st_ctime)
|
86
|
+
|
87
|
+
log_files = {
|
88
|
+
job_id: files
|
89
|
+
for log_path in logs
|
90
|
+
for job_id, files in self.upload_dir(log_path).items()
|
91
|
+
}
|
92
|
+
|
93
|
+
artifact_files = {
|
94
|
+
job_id: files
|
95
|
+
for artifact_path in artifacts
|
96
|
+
for job_id, files in self.upload_dir(artifact_path).items()
|
97
|
+
}
|
98
|
+
|
99
|
+
files_by_id = {
|
100
|
+
job_id: log_files.get(job_id, []) + artifact_files.get(job_id, [])
|
101
|
+
for job_id in log_files.keys() | artifact_files.keys()
|
102
|
+
}
|
33
103
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
files = None
|
38
|
-
has_walk = getattr(job_cache, "walk", None)
|
39
|
-
if has_walk:
|
40
|
-
files = sorted(
|
41
|
-
[
|
42
|
-
w_path / file
|
43
|
-
for w_path, _, w_files in job_cache.walk()
|
44
|
-
for file in w_files
|
45
|
-
],
|
46
|
-
key=lambda p: p.stat().st_size,
|
47
|
-
)
|
48
|
-
else:
|
49
|
-
files = sorted(
|
50
|
-
[
|
51
|
-
Path(Path(w_path) / file)
|
52
|
-
for w_path, _, w_files in os.walk(job_cache)
|
53
|
-
for file in w_files
|
54
|
-
],
|
55
|
-
key=lambda p: p.stat().st_size,
|
56
|
-
)
|
57
|
-
|
58
|
-
file_ids = []
|
59
|
-
for file in files:
|
60
|
-
upload_id = self.upload_file(
|
61
|
-
file,
|
62
|
-
prefix=str(PurePath(file).relative_to(job_cache.parent).parent),
|
63
|
-
job_run_id=job_run_id,
|
64
|
-
)
|
65
|
-
|
66
|
-
if upload_id:
|
67
|
-
file_ids.append(upload_id)
|
68
|
-
continue
|
69
|
-
|
70
|
-
logger.error(f"Unable to upload file {file}")
|
71
|
-
|
72
|
-
# Update job run
|
73
|
-
if len(file_ids) > 0:
|
74
|
-
self.primitive.jobs.job_run_update(id=job_run_id, file_ids=file_ids)
|
75
|
-
|
76
|
-
# Clean up job cache
|
77
|
-
shutil.rmtree(path=job_cache)
|
104
|
+
# Update job run
|
105
|
+
for job_id, files in files_by_id.items():
|
106
|
+
self.primitive.jobs.job_run_update(id=job_id, file_ids=files)
|
primitive/utils/cache.py
CHANGED
@@ -44,6 +44,20 @@ def get_artifacts_cache(cache_id: str = None) -> Path:
|
|
44
44
|
return artifacts_dir
|
45
45
|
|
46
46
|
|
47
|
+
def get_logs_cache(cache_id: str = None) -> Path:
|
48
|
+
cache_dir = get_cache_dir()
|
49
|
+
|
50
|
+
logs_dir = cache_dir / "logs"
|
51
|
+
|
52
|
+
if cache_id:
|
53
|
+
logs_dir = logs_dir / cache_id
|
54
|
+
|
55
|
+
if not logs_dir.exists():
|
56
|
+
logs_dir.mkdir(parents=True, exist_ok=True)
|
57
|
+
|
58
|
+
return logs_dir
|
59
|
+
|
60
|
+
|
47
61
|
def get_deps_cache() -> Path:
|
48
62
|
cache_dir = get_cache_dir()
|
49
63
|
|
primitive/utils/shell.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
from pathlib import Path
|
2
2
|
import subprocess
|
3
|
+
from typing import Dict
|
3
4
|
|
4
5
|
|
5
6
|
def add_path_to_shell(path: Path):
|
@@ -31,3 +32,27 @@ def add_path_to_shell(path: Path):
|
|
31
32
|
file.write(f"export PATH={path}:$PATH\n")
|
32
33
|
|
33
34
|
return True
|
35
|
+
|
36
|
+
|
37
|
+
def env_string_to_dict(env_str: str) -> Dict:
|
38
|
+
lines = env_str.splitlines()
|
39
|
+
|
40
|
+
current_key = None
|
41
|
+
current_value = []
|
42
|
+
env_dict = {}
|
43
|
+
for line in lines:
|
44
|
+
if "=" in line:
|
45
|
+
if current_key is not None:
|
46
|
+
env_dict[current_key] = "\n".join(current_value)
|
47
|
+
|
48
|
+
key, value = line.split("=", 1)
|
49
|
+
|
50
|
+
current_key = key
|
51
|
+
current_value = [value]
|
52
|
+
else:
|
53
|
+
current_value.append(line)
|
54
|
+
|
55
|
+
if current_key is not None:
|
56
|
+
env_dict[current_key] = "\n".join(current_value)
|
57
|
+
|
58
|
+
return env_dict
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: primitive
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.60
|
4
4
|
Project-URL: Documentation, https://github.com//primitivecorp/primitive-cli#readme
|
5
5
|
Project-URL: Issues, https://github.com//primitivecorp/primitive-cli/issues
|
6
6
|
Project-URL: Source, https://github.com//primitivecorp/primitive-cli
|
@@ -1,14 +1,14 @@
|
|
1
|
-
primitive/__about__.py,sha256=
|
1
|
+
primitive/__about__.py,sha256=ltk-zb4IMmLsM4NDisEz2bXWrL5uUzzqsV5yJM95Caw,130
|
2
2
|
primitive/__init__.py,sha256=bwKdgggKNVssJFVPfKSxqFMz4IxSr54WWbmiZqTMPNI,106
|
3
3
|
primitive/cli.py,sha256=CGmWiqqCLMHtHGOUPuf3tVO6VvChBZ1VdSwCCglnBgA,2582
|
4
4
|
primitive/client.py,sha256=p-5z1iGM8ZydIrkYf4R6b7Yna73oszlGdXim9-Zsbyk,2364
|
5
5
|
primitive/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
-
primitive/agent/actions.py,sha256=
|
6
|
+
primitive/agent/actions.py,sha256=B7d2oNMjtjUP2RhD-QnNDWNl3jHwjUDk5KLWQ2OnNQ4,5883
|
7
7
|
primitive/agent/commands.py,sha256=-dVDilELfkGfbZB7qfEPs77Dm1oT62qJj4tsIk4KoxI,254
|
8
|
-
primitive/agent/process.py,sha256=
|
9
|
-
primitive/agent/provision.py,sha256=
|
10
|
-
primitive/agent/runner.py,sha256=
|
11
|
-
primitive/agent/uploader.py,sha256=
|
8
|
+
primitive/agent/process.py,sha256=LVI-RB4a0YEuXUTYMXKL5Xi9euNwUI2nxj00mv8EFOg,2253
|
9
|
+
primitive/agent/provision.py,sha256=rmwnro1K5F8mwtd45XAq7RVQmpDWnbBCQ8X_qgWhm3M,1546
|
10
|
+
primitive/agent/runner.py,sha256=pX1KgYNxSxkSIDydsQa60C4zV2EvNaVXA9rQlG17wc8,7129
|
11
|
+
primitive/agent/uploader.py,sha256=La33T2csENWKRsNagog3ELYe6110jufMnb7owmrPsIk,3150
|
12
12
|
primitive/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
13
13
|
primitive/auth/actions.py,sha256=MPsG9LcKcOPwA7gZ9Ewk0PZJhTQvIrGfODdz4GxSzgA,999
|
14
14
|
primitive/auth/commands.py,sha256=JahUq0E2e7Xa-FX1WEUv7TgM6ieDvNH4VwRRtxAW7HE,2340
|
@@ -80,16 +80,16 @@ primitive/sim/commands.py,sha256=8PaOfL1MO6qxTn7mNVRnBU1X2wa3gk_mlbAhBW6MnI0,591
|
|
80
80
|
primitive/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
81
81
|
primitive/utils/actions.py,sha256=HOFrmM3-0A_A3NS84MqrZ6JmQEiiPSoDqEeuu6b_qfQ,196
|
82
82
|
primitive/utils/auth.py,sha256=TtJKTR6tLmNrtWbOjJI-KJh4ZSJ1uG7ApE9GcY63m00,836
|
83
|
-
primitive/utils/cache.py,sha256=
|
83
|
+
primitive/utils/cache.py,sha256=FHGmVWYLJFQOazpXXcEwI0YJEZbdkgG39nOLdOv6VNk,1575
|
84
84
|
primitive/utils/config.py,sha256=DlFM5Nglo22WPtbpZSVtH7NX-PTMaKYlcrUE7GPRG4c,1058
|
85
85
|
primitive/utils/files.py,sha256=Yv__bQes3YIlzhOT9kVxtYhoA5CmUjPSvphl9PZ41k4,867
|
86
86
|
primitive/utils/git.py,sha256=1qNOu8X-33CavmrD580BmrFhD_WVO9PGWHUUboXJR_g,663
|
87
87
|
primitive/utils/memory_size.py,sha256=4xfha21kW82nFvOTtDFx9Jk2ZQoEhkfXii-PGNTpIUk,3058
|
88
88
|
primitive/utils/printer.py,sha256=f1XUpqi5dkTL3GWvYRUGlSwtj2IxU1q745T4Fxo7Tn4,370
|
89
|
-
primitive/utils/shell.py,sha256
|
89
|
+
primitive/utils/shell.py,sha256=j7E1YwgNWw57dFHVfEbqRNVcPHX0xDefX2vFSNgeI_8,1648
|
90
90
|
primitive/utils/verible.py,sha256=r7c_hfqvL0UicMmIzK3Cy_BfZI1ZpcfBeLqKEWFWqJo,2252
|
91
|
-
primitive-0.1.
|
92
|
-
primitive-0.1.
|
93
|
-
primitive-0.1.
|
94
|
-
primitive-0.1.
|
95
|
-
primitive-0.1.
|
91
|
+
primitive-0.1.60.dist-info/METADATA,sha256=Ehe8DxemJilg_9uewSTTCQE9vobsu-P4EmSIKexXNLo,3782
|
92
|
+
primitive-0.1.60.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
93
|
+
primitive-0.1.60.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
|
94
|
+
primitive-0.1.60.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
|
95
|
+
primitive-0.1.60.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|