primitive 0.1.53__py3-none-any.whl → 0.1.55__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 -1
- primitive/agent/process.py +27 -32
- primitive/agent/runner.py +21 -15
- primitive/agent/uploader.py +30 -12
- primitive/files/actions.py +11 -1
- {primitive-0.1.53.dist-info → primitive-0.1.55.dist-info}/METADATA +1 -1
- {primitive-0.1.53.dist-info → primitive-0.1.55.dist-info}/RECORD +11 -11
- {primitive-0.1.53.dist-info → primitive-0.1.55.dist-info}/WHEEL +0 -0
- {primitive-0.1.53.dist-info → primitive-0.1.55.dist-info}/entry_points.txt +0 -0
- {primitive-0.1.53.dist-info → primitive-0.1.55.dist-info}/licenses/LICENSE.txt +0 -0
primitive/__about__.py
CHANGED
primitive/agent/actions.py
CHANGED
primitive/agent/process.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
from subprocess import Popen, PIPE
|
2
|
-
import threading
|
3
2
|
import shlex
|
3
|
+
import selectors
|
4
4
|
from loguru import logger
|
5
5
|
|
6
6
|
|
@@ -21,8 +21,7 @@ class Process:
|
|
21
21
|
|
22
22
|
def start(self):
|
23
23
|
# Start the process
|
24
|
-
|
25
|
-
logger.info(f"workdir: {self.workdir}")
|
24
|
+
self.sel = selectors.DefaultSelector()
|
26
25
|
self.process = Popen(
|
27
26
|
self.cmd,
|
28
27
|
env=self.env,
|
@@ -32,40 +31,33 @@ class Process:
|
|
32
31
|
text=True,
|
33
32
|
)
|
34
33
|
|
35
|
-
|
36
|
-
|
37
|
-
for line in iter(pipe.readline, ""):
|
38
|
-
if line:
|
39
|
-
logger.log(level, line.rstrip())
|
40
|
-
if level == "ERROR":
|
41
|
-
self._errors += 1
|
34
|
+
self.sel.register(self.process.stdout, selectors.EVENT_READ)
|
35
|
+
self.sel.register(self.process.stderr, selectors.EVENT_READ)
|
42
36
|
|
43
|
-
|
37
|
+
def probe_logs(self):
|
38
|
+
for key, _ in self.sel.select():
|
39
|
+
data = key.fileobj.readline()
|
40
|
+
if not data:
|
41
|
+
continue
|
44
42
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
target=log_output, args=(self.process.stderr, "ERROR")
|
51
|
-
)
|
52
|
-
|
53
|
-
# Start the threads
|
54
|
-
self.stdout_thread.start()
|
55
|
-
self.stderr_thread.start()
|
43
|
+
if key.fileobj is self.process.stdout:
|
44
|
+
logger.info(data.rstrip())
|
45
|
+
elif key.fileobj is self.process.stderr:
|
46
|
+
logger.error(data.rstrip())
|
47
|
+
self._errors += 1
|
56
48
|
|
57
49
|
def wait(self):
|
58
|
-
|
59
|
-
|
60
|
-
self.
|
61
|
-
|
62
|
-
|
63
|
-
|
50
|
+
while True:
|
51
|
+
self.probe_logs()
|
52
|
+
if not self.is_running():
|
53
|
+
break
|
54
|
+
|
55
|
+
return self.finish()
|
64
56
|
|
65
57
|
def run(self):
|
66
58
|
"""Start and wait for the process."""
|
67
59
|
self.start()
|
68
|
-
self.wait()
|
60
|
+
return self.wait()
|
69
61
|
|
70
62
|
def is_running(self):
|
71
63
|
"""Check if the process is still running."""
|
@@ -73,10 +65,13 @@ class Process:
|
|
73
65
|
|
74
66
|
def finish(self):
|
75
67
|
"""Make sure that logging finishes"""
|
76
|
-
self.
|
77
|
-
|
68
|
+
if self.process:
|
69
|
+
self.sel.unregister(self.process.stdout)
|
70
|
+
self.sel.unregister(self.process.stderr)
|
71
|
+
self.process.stdout.close()
|
72
|
+
self.process.stderr.close()
|
78
73
|
|
79
|
-
|
74
|
+
return self.process.poll()
|
80
75
|
|
81
76
|
def terminate(self):
|
82
77
|
"""Terminate the process."""
|
primitive/agent/runner.py
CHANGED
@@ -2,6 +2,7 @@ import yaml
|
|
2
2
|
import sys
|
3
3
|
import typing
|
4
4
|
import os
|
5
|
+
import threading
|
5
6
|
from time import sleep
|
6
7
|
from typing import TypedDict, Iterable, List, Optional, Dict
|
7
8
|
from pathlib import Path, PurePath
|
@@ -111,10 +112,8 @@ class AgentRunner:
|
|
111
112
|
logger.info(f"Beginning step {step['name']}")
|
112
113
|
|
113
114
|
# Define step proc
|
114
|
-
logger.info(f"first cmd: {step['cmd']}")
|
115
|
-
logger.info(f"first workdir: {Path(self.source_dir / step['workdir'])}")
|
116
115
|
proc = Process(
|
117
|
-
step["cmd"],
|
116
|
+
cmd=step["cmd"],
|
118
117
|
workdir=Path(self.source_dir / step["workdir"]),
|
119
118
|
env=environment,
|
120
119
|
)
|
@@ -127,24 +126,31 @@ class AgentRunner:
|
|
127
126
|
conclusion = "failure"
|
128
127
|
break
|
129
128
|
|
130
|
-
|
131
|
-
|
132
|
-
|
129
|
+
def status_check():
|
130
|
+
while proc.is_running():
|
131
|
+
# Check job status
|
132
|
+
status = self.primitive.jobs.get_job_status(self.job_id)
|
133
|
+
status_value = status["jobRun"]["status"]
|
133
134
|
|
134
|
-
|
135
|
+
# TODO: Should probably use request_cancelled or something
|
136
|
+
# once we change it, we'll have to call conclude w/ cancelled status
|
137
|
+
if status_value == "completed":
|
138
|
+
logger.warning("Job cancelled by user")
|
139
|
+
proc.terminate()
|
140
|
+
return
|
135
141
|
|
136
|
-
|
137
|
-
# once we change it, we'll have to call conclude w/ cancelled status
|
138
|
-
if status_value == "completed":
|
139
|
-
logger.warning("Job cancelled by user")
|
140
|
-
proc.terminate()
|
141
|
-
return
|
142
|
+
sleep(5)
|
142
143
|
|
143
|
-
|
144
|
+
status_thread = threading.Thread(target=status_check)
|
145
|
+
status_thread.start()
|
144
146
|
|
145
|
-
|
147
|
+
# Wait for proc to finish
|
148
|
+
returncode = proc.wait()
|
146
149
|
total_errors += proc.errors
|
147
150
|
|
151
|
+
# Wait for status check
|
152
|
+
status_thread.join()
|
153
|
+
|
148
154
|
# Collect artifacts
|
149
155
|
if "artifacts" in step:
|
150
156
|
self.collect_artifacts(step)
|
primitive/agent/uploader.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import typing
|
2
2
|
import shutil
|
3
|
+
from loguru import logger
|
3
4
|
from pathlib import Path, PurePath
|
4
5
|
from ..utils.cache import get_artifacts_cache
|
5
6
|
|
@@ -14,33 +15,50 @@ class Uploader:
|
|
14
15
|
):
|
15
16
|
self.primitive = primitive
|
16
17
|
|
17
|
-
def upload_file(self, path: Path, prefix: str) -> str:
|
18
|
-
file_upload_response = self.primitive.files.file_upload(
|
18
|
+
def upload_file(self, path: Path, prefix: str, job_run_id: str) -> str:
|
19
|
+
file_upload_response = self.primitive.files.file_upload(
|
20
|
+
path, key_prefix=prefix, job_run_id=job_run_id
|
21
|
+
)
|
19
22
|
return file_upload_response.json()["data"]["fileUpload"]["id"]
|
20
23
|
|
21
24
|
def scan(self) -> None:
|
22
25
|
# Scan artifacts directory
|
23
26
|
artifacts_dir = get_artifacts_cache()
|
24
27
|
|
25
|
-
subdirs =
|
26
|
-
job_cache for job_cache in artifacts_dir.iterdir() if job_cache.is_dir()
|
27
|
-
|
28
|
+
subdirs = sorted(
|
29
|
+
[job_cache for job_cache in artifacts_dir.iterdir() if job_cache.is_dir()],
|
30
|
+
key=lambda p: p.stat().st_ctime,
|
31
|
+
)
|
28
32
|
|
29
33
|
for job_cache in subdirs:
|
30
34
|
job_run_id = job_cache.name
|
31
|
-
|
35
|
+
|
36
|
+
files = sorted(
|
37
|
+
[
|
38
|
+
w_path / file
|
39
|
+
for w_path, _, w_files in job_cache.walk()
|
40
|
+
for file in w_files
|
41
|
+
],
|
42
|
+
key=lambda p: p.stat().st_size,
|
43
|
+
)
|
32
44
|
|
33
45
|
file_ids = []
|
34
46
|
for file in files:
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
)
|
47
|
+
upload_id = self.upload_file(
|
48
|
+
file,
|
49
|
+
prefix=str(PurePath(file).relative_to(job_cache.parent).parent),
|
50
|
+
job_run_id=job_run_id,
|
40
51
|
)
|
41
52
|
|
53
|
+
if upload_id:
|
54
|
+
file_ids.append(upload_id)
|
55
|
+
continue
|
56
|
+
|
57
|
+
logger.error(f"Unable to upload file {file}")
|
58
|
+
|
42
59
|
# Update job run
|
43
|
-
|
60
|
+
if len(file_ids) > 0:
|
61
|
+
self.primitive.jobs.job_run_update(id=job_run_id, file_ids=file_ids)
|
44
62
|
|
45
63
|
# Clean up job cache
|
46
64
|
shutil.rmtree(path=job_cache)
|
primitive/files/actions.py
CHANGED
@@ -43,7 +43,13 @@ class Files(BaseAction):
|
|
43
43
|
return result
|
44
44
|
|
45
45
|
@guard
|
46
|
-
def file_upload(
|
46
|
+
def file_upload(
|
47
|
+
self,
|
48
|
+
path: Path,
|
49
|
+
is_public: bool = False,
|
50
|
+
key_prefix: str = "",
|
51
|
+
job_run_id: str = "",
|
52
|
+
):
|
47
53
|
file_path = str(path.resolve())
|
48
54
|
if path.exists() is False:
|
49
55
|
raise FileNotFoundError(f"File not found at {file_path}")
|
@@ -54,6 +60,8 @@ class Files(BaseAction):
|
|
54
60
|
+ file_path
|
55
61
|
+ """\", "keyPrefix": \""""
|
56
62
|
+ key_prefix
|
63
|
+
+ """\", "jobRunId": \""""
|
64
|
+
+ job_run_id
|
57
65
|
+ """\" } } }"""
|
58
66
|
) # noqa
|
59
67
|
|
@@ -63,6 +71,8 @@ class Files(BaseAction):
|
|
63
71
|
+ file_path
|
64
72
|
+ """\", "keyPrefix": \""""
|
65
73
|
+ key_prefix
|
74
|
+
+ """\", "jobRunId": \""""
|
75
|
+
+ job_run_id
|
66
76
|
+ """\" } } }"""
|
67
77
|
) # noqa
|
68
78
|
body = {
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: primitive
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.55
|
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,13 +1,13 @@
|
|
1
|
-
primitive/__about__.py,sha256=
|
1
|
+
primitive/__about__.py,sha256=NkPE6ibF3SMDPKeHuwyX-BngPvgNwRJhl1PjxPpV5XQ,130
|
2
2
|
primitive/__init__.py,sha256=bwKdgggKNVssJFVPfKSxqFMz4IxSr54WWbmiZqTMPNI,106
|
3
3
|
primitive/cli.py,sha256=VQPSewC6ouGdEG9W1gllawGJTydpOY0Lzg7LURXcqQg,2374
|
4
4
|
primitive/client.py,sha256=vSJkifx450czuLvu0f2o-viSCC0p2f1UicA-2P5cJAw,2188
|
5
|
-
primitive/agent/actions.py,sha256=
|
5
|
+
primitive/agent/actions.py,sha256=m1FGvtOHdrZRP-YC31bFoUj-ZunXlFFW4NnPH9Bpzc0,5499
|
6
6
|
primitive/agent/commands.py,sha256=-dVDilELfkGfbZB7qfEPs77Dm1oT62qJj4tsIk4KoxI,254
|
7
|
-
primitive/agent/process.py,sha256=
|
7
|
+
primitive/agent/process.py,sha256=2ZY3YoJHvoukrsCAZIt-AF2YKY4HEO5_jWji5K3W9fM,2267
|
8
8
|
primitive/agent/provision.py,sha256=3EEzOV-ria6zf-pvfNddad1lzzd1QmfKInTIjmwX71Y,1673
|
9
|
-
primitive/agent/runner.py,sha256=
|
10
|
-
primitive/agent/uploader.py,sha256=
|
9
|
+
primitive/agent/runner.py,sha256=hDh6ip_Jb0EsvygrB7-GWVTOf-jN8E7H0RgCGOdQnPU,6634
|
10
|
+
primitive/agent/uploader.py,sha256=Yiaszf44euL-spYe4lluDtgiPRi_0nq5l3eQtrWQ8bM,1903
|
11
11
|
primitive/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
12
12
|
primitive/auth/actions.py,sha256=2vZEC3LLAXj6eBBmt_2OEDEcBIb3uLkkNjgbZTIaQsY,1095
|
13
13
|
primitive/auth/commands.py,sha256=JahUq0E2e7Xa-FX1WEUv7TgM6ieDvNH4VwRRtxAW7HE,2340
|
@@ -15,7 +15,7 @@ primitive/daemons/actions.py,sha256=Nt3yNtbBhen0jK4sRsH_N7AP3UBuyL48VaUhtC7wYq8,
|
|
15
15
|
primitive/daemons/commands.py,sha256=-Muh-6ib4uAVtPn_67AcMrDwuCwYlCnRQozCi2Xurmk,1726
|
16
16
|
primitive/daemons/launch_agents.py,sha256=qovt32gwpjGDd82z_SY5EGCUjaUyNA49pZFajZsw3eE,4796
|
17
17
|
primitive/daemons/launch_service.py,sha256=FPB9qKEjhllRfEpct0ng2L9lpIaGJbQwn1JdFT8uBA8,5600
|
18
|
-
primitive/files/actions.py,sha256=
|
18
|
+
primitive/files/actions.py,sha256=3AOGLEUeplNlkKkyi1QajzxMc93Adh5jRQ8SQHFlJeU,2889
|
19
19
|
primitive/files/commands.py,sha256=DDizo3xJnU3KLUBTMeeM72viVpnJinLwxs84tmqKhqo,810
|
20
20
|
primitive/git/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
21
21
|
primitive/git/actions.py,sha256=OTuGRXfjop9u1LtNaBen87mEU6ROeoa_MDgBbB2l6ig,1428
|
@@ -46,8 +46,8 @@ primitive/utils/memory_size.py,sha256=4xfha21kW82nFvOTtDFx9Jk2ZQoEhkfXii-PGNTpIU
|
|
46
46
|
primitive/utils/printer.py,sha256=f1XUpqi5dkTL3GWvYRUGlSwtj2IxU1q745T4Fxo7Tn4,370
|
47
47
|
primitive/utils/shell.py,sha256=-7UjQaBqSGHzEEyX8pNjeYFFP0P3lVnDV0OkgPz1qHU,1050
|
48
48
|
primitive/utils/verible.py,sha256=r7c_hfqvL0UicMmIzK3Cy_BfZI1ZpcfBeLqKEWFWqJo,2252
|
49
|
-
primitive-0.1.
|
50
|
-
primitive-0.1.
|
51
|
-
primitive-0.1.
|
52
|
-
primitive-0.1.
|
53
|
-
primitive-0.1.
|
49
|
+
primitive-0.1.55.dist-info/METADATA,sha256=f8IGnZPje8mcvN8YQ-JhQbfKxaeHAwF5FdU7dsSuxtc,3782
|
50
|
+
primitive-0.1.55.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
51
|
+
primitive-0.1.55.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
|
52
|
+
primitive-0.1.55.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
|
53
|
+
primitive-0.1.55.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|