primitive 0.1.60__py3-none-any.whl → 0.1.62__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/runner.py +2 -1
- primitive/agent/uploader.py +20 -22
- primitive/exec/actions.py +47 -1
- primitive/exec/commands.py +1 -1
- primitive/exec/interactive.py +99 -0
- primitive/files/actions.py +125 -17
- primitive/files/commands.py +17 -5
- primitive/files/graphql/mutations.py +27 -6
- primitive/hardware/actions.py +14 -1
- primitive/hardware/graphql/queries.py +10 -0
- primitive/sim/actions.py +5 -9
- primitive/utils/verible.py +3 -3
- {primitive-0.1.60.dist-info → primitive-0.1.62.dist-info}/METADATA +2 -1
- {primitive-0.1.60.dist-info → primitive-0.1.62.dist-info}/RECORD +18 -17
- {primitive-0.1.60.dist-info → primitive-0.1.62.dist-info}/WHEEL +0 -0
- {primitive-0.1.60.dist-info → primitive-0.1.62.dist-info}/entry_points.txt +0 -0
- {primitive-0.1.60.dist-info → primitive-0.1.62.dist-info}/licenses/LICENSE.txt +0 -0
primitive/__about__.py
CHANGED
primitive/agent/runner.py
CHANGED
@@ -191,7 +191,8 @@ class AgentRunner:
|
|
191
191
|
logger.remove(self.logger_handle)
|
192
192
|
|
193
193
|
self.logger_handle = logger.add(
|
194
|
-
Path(self.logs_dir / f"{label}_{{time}}.primitive.log"),
|
194
|
+
Path(self.logs_dir / f"{label}_{{time}}.primitive.log"),
|
195
|
+
rotation=self.max_log_size,
|
195
196
|
)
|
196
197
|
|
197
198
|
def provision(self) -> Optional[Dict]:
|
primitive/agent/uploader.py
CHANGED
@@ -2,8 +2,10 @@ import typing
|
|
2
2
|
from typing import Dict
|
3
3
|
import shutil
|
4
4
|
import os
|
5
|
-
from loguru import logger
|
6
5
|
from pathlib import Path, PurePath
|
6
|
+
|
7
|
+
from loguru import logger
|
8
|
+
|
7
9
|
from ..utils.cache import get_artifacts_cache, get_logs_cache
|
8
10
|
|
9
11
|
if typing.TYPE_CHECKING:
|
@@ -17,12 +19,6 @@ class Uploader:
|
|
17
19
|
):
|
18
20
|
self.primitive = primitive
|
19
21
|
|
20
|
-
def upload_file(self, path: Path, prefix: str, job_run_id: str) -> str:
|
21
|
-
file_upload_response = self.primitive.files.file_upload(
|
22
|
-
path, key_prefix=prefix, job_run_id=job_run_id
|
23
|
-
)
|
24
|
-
return file_upload_response.json()["data"]["fileUpload"]["id"]
|
25
|
-
|
26
22
|
def upload_dir(self, cache: Path) -> Dict:
|
27
23
|
file_ids = []
|
28
24
|
job_run_id = cache.name
|
@@ -49,11 +45,12 @@ class Uploader:
|
|
49
45
|
)
|
50
46
|
|
51
47
|
for file in files:
|
52
|
-
|
53
|
-
file,
|
54
|
-
|
48
|
+
response = self.primitive.files.upload_file_via_api(
|
49
|
+
path=file,
|
50
|
+
key_prefix=str(PurePath(file).relative_to(cache.parent).parent),
|
55
51
|
job_run_id=job_run_id,
|
56
52
|
)
|
53
|
+
upload_id = response.json()["data"]["fileUpload"]["id"]
|
57
54
|
|
58
55
|
if upload_id:
|
59
56
|
file_ids.append(upload_id)
|
@@ -66,23 +63,24 @@ class Uploader:
|
|
66
63
|
|
67
64
|
return {job_run_id: file_ids}
|
68
65
|
|
69
|
-
|
70
66
|
def scan(self) -> None:
|
71
67
|
# Scan artifacts directory
|
72
68
|
artifacts_dir = get_artifacts_cache()
|
73
69
|
logs_dir = get_logs_cache()
|
74
70
|
|
75
|
-
artifacts = sorted(
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
71
|
+
artifacts = sorted(
|
72
|
+
[
|
73
|
+
artifacts_cache
|
74
|
+
for artifacts_cache in artifacts_dir.iterdir()
|
75
|
+
if artifacts_cache.is_dir()
|
76
|
+
],
|
77
|
+
key=lambda p: p.stat().st_ctime,
|
78
|
+
)
|
80
79
|
|
81
|
-
logs = sorted(
|
82
|
-
logs_cache
|
83
|
-
|
84
|
-
|
85
|
-
], key=lambda p: p.stat().st_ctime)
|
80
|
+
logs = sorted(
|
81
|
+
[logs_cache for logs_cache in logs_dir.iterdir() if logs_cache.is_dir()],
|
82
|
+
key=lambda p: p.stat().st_ctime,
|
83
|
+
)
|
86
84
|
|
87
85
|
log_files = {
|
88
86
|
job_id: files
|
@@ -97,7 +95,7 @@ class Uploader:
|
|
97
95
|
}
|
98
96
|
|
99
97
|
files_by_id = {
|
100
|
-
job_id: log_files.get(job_id, []) + artifact_files.get(job_id, [])
|
98
|
+
job_id: log_files.get(job_id, []) + artifact_files.get(job_id, [])
|
101
99
|
for job_id in log_files.keys() | artifact_files.keys()
|
102
100
|
}
|
103
101
|
|
primitive/exec/actions.py
CHANGED
@@ -1,9 +1,14 @@
|
|
1
|
+
import tempfile
|
1
2
|
import typing
|
2
3
|
|
4
|
+
from primitive.exec.interactive import interactive_shell
|
5
|
+
|
3
6
|
if typing.TYPE_CHECKING:
|
4
7
|
pass
|
5
8
|
|
6
9
|
|
10
|
+
from paramiko import SSHClient
|
11
|
+
|
7
12
|
from primitive.utils.actions import BaseAction
|
8
13
|
|
9
14
|
|
@@ -41,7 +46,48 @@ class Exec(BaseAction):
|
|
41
46
|
reservation_id=reservation["id"], desired_status="in_progress"
|
42
47
|
)
|
43
48
|
|
44
|
-
|
49
|
+
ssh_credentials = self.primitive.hardware.get_hardware_ssh_credentials(
|
50
|
+
hardware_id=hardware["id"]
|
51
|
+
)
|
52
|
+
|
53
|
+
ssh_hostname = ssh_credentials["ssh_hostname"]
|
54
|
+
ssh_username = ssh_credentials["ssh_username"]
|
55
|
+
ssh_password = ssh_credentials["ssh_password"]
|
56
|
+
ssh_private_key = ssh_credentials["ssh_private_key"]
|
57
|
+
|
58
|
+
ssh_client = SSHClient()
|
59
|
+
ssh_client.load_system_host_keys()
|
60
|
+
keyfile = None
|
61
|
+
if ssh_private_key:
|
62
|
+
keyfile = tempfile.NamedTemporaryFile()
|
63
|
+
keyfile.write(ssh_private_key.encode())
|
64
|
+
keyfile.flush()
|
65
|
+
ssh_client.connect(
|
66
|
+
hostname=ssh_hostname,
|
67
|
+
username=ssh_username,
|
68
|
+
key_filename=keyfile.name,
|
69
|
+
)
|
70
|
+
else:
|
71
|
+
ssh_client.connect(
|
72
|
+
hostname=ssh_hostname,
|
73
|
+
username=ssh_username,
|
74
|
+
pkey=ssh_private_key,
|
75
|
+
password=ssh_password,
|
76
|
+
)
|
77
|
+
|
78
|
+
if command:
|
79
|
+
formatted_command = " ".join(command)
|
80
|
+
stdin, stdout, stderr = ssh_client.exec_command(formatted_command)
|
81
|
+
print(stdout.read())
|
82
|
+
ssh_client.close()
|
83
|
+
else:
|
84
|
+
channel = ssh_client.get_transport().open_session()
|
85
|
+
channel.get_pty()
|
86
|
+
channel.invoke_shell()
|
87
|
+
interactive_shell(channel)
|
88
|
+
ssh_client.close()
|
89
|
+
if keyfile:
|
90
|
+
keyfile.close()
|
45
91
|
|
46
92
|
if created_reservation_on_behalf_of_user:
|
47
93
|
print("Cleaning up reservation.")
|
primitive/exec/commands.py
CHANGED
@@ -13,7 +13,7 @@ if typing.TYPE_CHECKING:
|
|
13
13
|
type=str,
|
14
14
|
required=True,
|
15
15
|
)
|
16
|
-
@click.argument("command", nargs=-1, required=
|
16
|
+
@click.argument("command", nargs=-1, required=False)
|
17
17
|
def cli(context, hardware_identifier: str, command: str) -> None:
|
18
18
|
"""Exec"""
|
19
19
|
primitive: Primitive = context.obj.get("PRIMITIVE")
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# https://github.com/paramiko/paramiko/blob/main/demos/interactive.py
|
2
|
+
|
3
|
+
import socket
|
4
|
+
import sys
|
5
|
+
|
6
|
+
|
7
|
+
def _to_unicode(s):
|
8
|
+
"""
|
9
|
+
decode a string as ascii or utf8 if possible (as required by the sftp
|
10
|
+
protocol). if neither works, just return a byte string because the server
|
11
|
+
probably doesn't know the filename's encoding.
|
12
|
+
"""
|
13
|
+
try:
|
14
|
+
return s.encode("ascii")
|
15
|
+
except (UnicodeError, AttributeError):
|
16
|
+
try:
|
17
|
+
return s.decode("utf-8")
|
18
|
+
except UnicodeError:
|
19
|
+
return s
|
20
|
+
|
21
|
+
|
22
|
+
# windows does not have termios...
|
23
|
+
try:
|
24
|
+
import termios
|
25
|
+
import tty
|
26
|
+
|
27
|
+
has_termios = True
|
28
|
+
except ImportError:
|
29
|
+
has_termios = False
|
30
|
+
|
31
|
+
|
32
|
+
def interactive_shell(chan):
|
33
|
+
if has_termios:
|
34
|
+
posix_shell(chan)
|
35
|
+
else:
|
36
|
+
windows_shell(chan)
|
37
|
+
|
38
|
+
|
39
|
+
def posix_shell(chan):
|
40
|
+
import select
|
41
|
+
|
42
|
+
oldtty = termios.tcgetattr(sys.stdin)
|
43
|
+
try:
|
44
|
+
tty.setraw(sys.stdin.fileno())
|
45
|
+
tty.setcbreak(sys.stdin.fileno())
|
46
|
+
chan.settimeout(0.0)
|
47
|
+
|
48
|
+
while True:
|
49
|
+
r, w, e = select.select([chan, sys.stdin], [], [])
|
50
|
+
if chan in r:
|
51
|
+
try:
|
52
|
+
x = _to_unicode(chan.recv(1024))
|
53
|
+
if len(x) == 0:
|
54
|
+
sys.stdout.write("\r\n*** EOF\r\n")
|
55
|
+
break
|
56
|
+
sys.stdout.write(x)
|
57
|
+
sys.stdout.flush()
|
58
|
+
except socket.timeout:
|
59
|
+
pass
|
60
|
+
if sys.stdin in r:
|
61
|
+
x = sys.stdin.read(1)
|
62
|
+
if len(x) == 0:
|
63
|
+
break
|
64
|
+
chan.send(x)
|
65
|
+
|
66
|
+
finally:
|
67
|
+
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, oldtty)
|
68
|
+
|
69
|
+
|
70
|
+
# thanks to Mike Looijmans for this code
|
71
|
+
def windows_shell(chan):
|
72
|
+
import threading
|
73
|
+
|
74
|
+
sys.stdout.write(
|
75
|
+
"Line-buffered terminal emulation. Press F6 or ^Z to send EOF.\r\n\r\n"
|
76
|
+
)
|
77
|
+
|
78
|
+
def writeall(sock):
|
79
|
+
while True:
|
80
|
+
data = sock.recv(256)
|
81
|
+
if not data:
|
82
|
+
sys.stdout.write("\r\n*** EOF ***\r\n\r\n")
|
83
|
+
sys.stdout.flush()
|
84
|
+
break
|
85
|
+
sys.stdout.write(data)
|
86
|
+
sys.stdout.flush()
|
87
|
+
|
88
|
+
writer = threading.Thread(target=writeall, args=(chan,))
|
89
|
+
writer.start()
|
90
|
+
|
91
|
+
try:
|
92
|
+
while True:
|
93
|
+
d = sys.stdin.read(1)
|
94
|
+
if not d:
|
95
|
+
break
|
96
|
+
chan.send(d)
|
97
|
+
except EOFError:
|
98
|
+
# user hit ^Z or F6
|
99
|
+
pass
|
primitive/files/actions.py
CHANGED
@@ -1,34 +1,85 @@
|
|
1
|
+
import hashlib
|
2
|
+
import sys
|
3
|
+
import threading
|
1
4
|
from pathlib import Path
|
5
|
+
from typing import Dict, Optional
|
2
6
|
|
7
|
+
import requests
|
3
8
|
from gql import gql
|
9
|
+
from loguru import logger
|
4
10
|
|
5
11
|
from primitive.graphql.sdk import create_requests_session
|
6
12
|
from primitive.utils.actions import BaseAction
|
7
13
|
|
8
14
|
from ..utils.auth import guard
|
9
|
-
from .graphql.mutations import
|
15
|
+
from .graphql.mutations import (
|
16
|
+
file_update_mutation,
|
17
|
+
pending_file_create_mutation,
|
18
|
+
)
|
19
|
+
|
20
|
+
|
21
|
+
# this class can be used in multithreaded S3 client uploader
|
22
|
+
# this requires getting an S3 access token to this machine however
|
23
|
+
# we are using presigned urls instead at this time Oct 29th, 2024
|
24
|
+
class ProgressPercentage(object):
|
25
|
+
def __init__(self, filepath: Path) -> None:
|
26
|
+
self._filename = filepath.name
|
27
|
+
self._size = float(filepath.stat().st_size)
|
28
|
+
self._seen_so_far = 0
|
29
|
+
self._lock = threading.Lock()
|
30
|
+
|
31
|
+
def __call__(self, bytes_amount):
|
32
|
+
# To simplify, assume this is hooked up to a single filename
|
33
|
+
with self._lock:
|
34
|
+
self._seen_so_far += bytes_amount
|
35
|
+
percentage = (self._seen_so_far / self._size) * 100
|
36
|
+
sys.stdout.write(
|
37
|
+
"\r%s %s / %s (%.2f%%)"
|
38
|
+
% (self._filename, self._seen_so_far, self._size, percentage)
|
39
|
+
)
|
40
|
+
sys.stdout.flush()
|
10
41
|
|
11
42
|
|
12
43
|
class Files(BaseAction):
|
13
|
-
|
14
|
-
def trace_create(
|
44
|
+
def _pending_file_create(
|
15
45
|
self,
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
46
|
+
file_name: str,
|
47
|
+
file_size: int,
|
48
|
+
file_checksum: str,
|
49
|
+
file_path: str,
|
50
|
+
key_prefix: str,
|
51
|
+
is_public: bool = False,
|
22
52
|
):
|
23
|
-
mutation = gql(
|
53
|
+
mutation = gql(pending_file_create_mutation)
|
24
54
|
input = {
|
25
|
-
"
|
26
|
-
"
|
27
|
-
"
|
28
|
-
"
|
29
|
-
"
|
30
|
-
"
|
55
|
+
"filePath": file_path,
|
56
|
+
"fileName": file_name,
|
57
|
+
"fileSize": file_size,
|
58
|
+
"fileChecksum": file_checksum,
|
59
|
+
"keyPrefix": key_prefix,
|
60
|
+
"isPublic": is_public,
|
61
|
+
}
|
62
|
+
variables = {"input": input}
|
63
|
+
result = self.primitive.session.execute(
|
64
|
+
mutation, variable_values=variables, get_execution_result=True
|
65
|
+
)
|
66
|
+
return result.data.get("pendingFileCreate")
|
67
|
+
|
68
|
+
def _update_file_status(
|
69
|
+
self,
|
70
|
+
file_id: str,
|
71
|
+
is_uploading: Optional[bool] = None,
|
72
|
+
is_complete: Optional[bool] = None,
|
73
|
+
):
|
74
|
+
mutation = gql(file_update_mutation)
|
75
|
+
input: Dict[str, str | bool] = {
|
76
|
+
"id": file_id,
|
31
77
|
}
|
78
|
+
if is_uploading is not None:
|
79
|
+
input["isUploading"] = is_uploading
|
80
|
+
if is_complete is not None:
|
81
|
+
input["isComplete"] = is_complete
|
82
|
+
|
32
83
|
variables = {"input": input}
|
33
84
|
result = self.primitive.session.execute(
|
34
85
|
mutation, variable_values=variables, get_execution_result=True
|
@@ -36,13 +87,70 @@ class Files(BaseAction):
|
|
36
87
|
return result
|
37
88
|
|
38
89
|
@guard
|
39
|
-
def
|
90
|
+
def upload_file_direct(
|
91
|
+
self,
|
92
|
+
path: Path,
|
93
|
+
is_public: False,
|
94
|
+
key_prefix: str = "",
|
95
|
+
file_id: Optional[str] = None,
|
96
|
+
):
|
97
|
+
logger.enable("primitive")
|
98
|
+
if path.exists() is False:
|
99
|
+
raise Exception(f"File {path} does not exist.")
|
100
|
+
|
101
|
+
file_size = path.stat().st_size
|
102
|
+
if file_size == 0:
|
103
|
+
raise Exception(f"{path} is empty.")
|
104
|
+
|
105
|
+
file_checksum = hashlib.md5(path.read_bytes()).hexdigest()
|
106
|
+
|
107
|
+
if not file_id:
|
108
|
+
pending_file_create = self._pending_file_create(
|
109
|
+
file_name=path.name,
|
110
|
+
file_size=path.stat().st_size,
|
111
|
+
file_checksum=file_checksum,
|
112
|
+
file_path=str(path),
|
113
|
+
key_prefix=key_prefix,
|
114
|
+
is_public=is_public,
|
115
|
+
)
|
116
|
+
file_id = pending_file_create.get("id")
|
117
|
+
presigned_url = pending_file_create.get("presignedUrlForUpload")
|
118
|
+
|
119
|
+
if not file_id:
|
120
|
+
raise Exception("No file_id found or provided.")
|
121
|
+
if not presigned_url:
|
122
|
+
raise Exception("No presigned_url returned.")
|
123
|
+
|
124
|
+
self._update_file_status(file_id, is_uploading=True)
|
125
|
+
with open(path, "rb") as object_file:
|
126
|
+
object_text = object_file.read()
|
127
|
+
response = requests.put(presigned_url, data=object_text)
|
128
|
+
if response.ok:
|
129
|
+
logger.info(f"File {path} uploaded successfully.")
|
130
|
+
update_file_status_result = self._update_file_status(
|
131
|
+
file_id, is_uploading=False, is_complete=True
|
132
|
+
)
|
133
|
+
else:
|
134
|
+
message = f"Failed to upload file {path}. {response.status_code}: {response.text}"
|
135
|
+
logger.error(message)
|
136
|
+
raise Exception(message)
|
137
|
+
file_pk = update_file_status_result.data.get("fileUpdate").get("pk")
|
138
|
+
file_access_url = f"{self.primitive.host_config.get("transport")}://{self.primitive.host}/files/{file_pk}/presigned-url/"
|
139
|
+
logger.info(f"Available at: {file_access_url}")
|
140
|
+
return update_file_status_result
|
141
|
+
|
142
|
+
@guard
|
143
|
+
def upload_file_via_api(
|
40
144
|
self,
|
41
145
|
path: Path,
|
42
146
|
is_public: bool = False,
|
43
147
|
key_prefix: str = "",
|
44
148
|
job_run_id: str = "",
|
45
149
|
):
|
150
|
+
"""
|
151
|
+
This method uploads a file via the Primitive API.
|
152
|
+
This does NOT upload the file straight to S3
|
153
|
+
"""
|
46
154
|
file_path = str(path.resolve())
|
47
155
|
if path.exists() is False:
|
48
156
|
raise FileNotFoundError(f"File not found at {file_path}")
|
primitive/files/commands.py
CHANGED
@@ -1,7 +1,10 @@
|
|
1
|
+
import json
|
2
|
+
import typing
|
3
|
+
from pathlib import Path
|
4
|
+
|
1
5
|
import click
|
6
|
+
|
2
7
|
from ..utils.printer import print_result
|
3
|
-
from pathlib import Path
|
4
|
-
import typing
|
5
8
|
|
6
9
|
if typing.TYPE_CHECKING:
|
7
10
|
from ..client import Primitive
|
@@ -19,10 +22,19 @@ def cli(context):
|
|
19
22
|
@click.argument("path", type=click.Path(exists=True))
|
20
23
|
@click.option("--public", "-p", help="Is this a Public file", is_flag=True)
|
21
24
|
@click.option("--key-prefix", "-k", help="Key Prefix", default="")
|
22
|
-
|
25
|
+
@click.option("--direct", "-k", help="direct", is_flag=True)
|
26
|
+
def file_upload_command(context, path, public, key_prefix, direct):
|
23
27
|
"""File Upload"""
|
24
28
|
primitive: Primitive = context.obj.get("PRIMITIVE")
|
25
29
|
path = Path(path)
|
26
|
-
|
27
|
-
|
30
|
+
if direct:
|
31
|
+
result = primitive.files.upload_file_direct(
|
32
|
+
path, is_public=public, key_prefix=key_prefix
|
33
|
+
)
|
34
|
+
else:
|
35
|
+
result = primitive.files.upload_file_via_api(
|
36
|
+
path, is_public=public, key_prefix=key_prefix
|
37
|
+
)
|
38
|
+
|
39
|
+
message = json.dumps(result.data)
|
28
40
|
print_result(message=message, context=context)
|
@@ -1,11 +1,32 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
from primitive.graphql.utility_fragments import operation_info_fragment
|
2
|
+
|
3
|
+
file_update_mutation = (
|
4
|
+
operation_info_fragment
|
5
|
+
+ """
|
6
|
+
mutation fileUpdate($input: FileInputPartial!) {
|
7
|
+
fileUpdate(input: $input) {
|
8
|
+
... on File {
|
5
9
|
id
|
6
|
-
|
7
|
-
signalName
|
10
|
+
pk
|
8
11
|
}
|
12
|
+
...OperationInfoFragment
|
9
13
|
}
|
10
14
|
}
|
11
15
|
"""
|
16
|
+
)
|
17
|
+
|
18
|
+
pending_file_create_mutation = (
|
19
|
+
operation_info_fragment
|
20
|
+
+ """
|
21
|
+
mutation pendingFileCreate($input: PendingFileCreateInput!) {
|
22
|
+
pendingFileCreate(input: $input) {
|
23
|
+
... on File {
|
24
|
+
id
|
25
|
+
pk
|
26
|
+
presignedUrlForUpload
|
27
|
+
}
|
28
|
+
...OperationInfoFragment
|
29
|
+
}
|
30
|
+
}
|
31
|
+
"""
|
32
|
+
)
|
primitive/hardware/actions.py
CHANGED
@@ -22,7 +22,7 @@ from .graphql.mutations import (
|
|
22
22
|
hardware_update_mutation,
|
23
23
|
register_hardware_mutation,
|
24
24
|
)
|
25
|
-
from .graphql.queries import hardware_list
|
25
|
+
from .graphql.queries import hardware_list, hardware_ssh_credentials
|
26
26
|
|
27
27
|
if typing.TYPE_CHECKING:
|
28
28
|
pass
|
@@ -479,3 +479,16 @@ class Hardware(BaseAction):
|
|
479
479
|
raise Exception(f"No hardware found with slug {hardware_identifier}")
|
480
480
|
|
481
481
|
return hardware
|
482
|
+
|
483
|
+
@guard
|
484
|
+
def get_hardware_ssh_credentials(self, hardware_id: str):
|
485
|
+
query = gql(hardware_ssh_credentials)
|
486
|
+
|
487
|
+
variables = {
|
488
|
+
"id": hardware_id,
|
489
|
+
}
|
490
|
+
result = self.primitive.session.execute(
|
491
|
+
query, variable_values=variables, get_execution_result=True
|
492
|
+
)
|
493
|
+
ssh_credentials = result.data.get("hardware").get("sshCredentials")
|
494
|
+
return ssh_credentials
|
primitive/sim/actions.py
CHANGED
@@ -46,10 +46,6 @@ class Sim(BaseAction):
|
|
46
46
|
|
47
47
|
return True, message
|
48
48
|
|
49
|
-
def upload_file(self, path: Path, prefix: str) -> str:
|
50
|
-
file_upload_response = self.primitive.files.file_upload(path, key_prefix=prefix)
|
51
|
-
return file_upload_response.json()["data"]["fileUpload"]["id"]
|
52
|
-
|
53
49
|
def collect_artifacts(self, source: Path, job_run_id: str) -> None:
|
54
50
|
# Parse VCD artifacts using rust binding
|
55
51
|
# TODO: eventually make this smarter, only parsing VCDs for failed tests
|
@@ -72,12 +68,12 @@ class Sim(BaseAction):
|
|
72
68
|
)
|
73
69
|
for file_path in files:
|
74
70
|
try:
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
prefix=f"{job_run_id}/{str(PurePath(file_path).relative_to(Path(source)).parent)}",
|
79
|
-
)
|
71
|
+
key_prefix = f"{job_run_id}/{str(PurePath(file_path).relative_to(Path(source)).parent)}"
|
72
|
+
file_upload_response = self.primitive.files.upload_file_via_api(
|
73
|
+
file_path, key_prefix=key_prefix
|
80
74
|
)
|
75
|
+
file_id = file_upload_response.json()["data"]["fileUpload"]["id"]
|
76
|
+
file_ids.append(file_id)
|
81
77
|
except FileNotFoundError:
|
82
78
|
logger.warning(f"{file_path} not found...")
|
83
79
|
|
primitive/utils/verible.py
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
import tarfile
|
2
|
-
import requests
|
3
|
-
from .shell import add_path_to_shell
|
4
|
-
from .cache import get_deps_cache
|
5
2
|
|
3
|
+
import requests
|
6
4
|
from loguru import logger
|
7
5
|
|
6
|
+
from .cache import get_deps_cache
|
7
|
+
from .shell import add_path_to_shell
|
8
8
|
|
9
9
|
VERIBLE_MAC_OS_LINK = "https://github.com/chipsalliance/verible/releases/download/v0.0-3752-g8b64887e/verible-v0.0-3752-g8b64887e-macOS.tar.gz"
|
10
10
|
VERIBLE_WINDOWS_64_OS_LINK = "https://github.com/chipsalliance/verible/releases/download/v0.0-3752-g8b64887e/verible-v0.0-3752-g8b64887e-win64.zip"
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: primitive
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.62
|
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
|
@@ -21,6 +21,7 @@ Requires-Python: >=3.11
|
|
21
21
|
Requires-Dist: click
|
22
22
|
Requires-Dist: gql[all]
|
23
23
|
Requires-Dist: loguru
|
24
|
+
Requires-Dist: paramiko[all]
|
24
25
|
Requires-Dist: primitive-pal==0.1.4
|
25
26
|
Requires-Dist: pyyaml
|
26
27
|
Description-Content-Type: text/markdown
|
@@ -1,4 +1,4 @@
|
|
1
|
-
primitive/__about__.py,sha256=
|
1
|
+
primitive/__about__.py,sha256=geKDLLFDQydMN_sAK9SDHgrotZtzInxl2yNgIL4tHng,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
|
@@ -7,8 +7,8 @@ primitive/agent/actions.py,sha256=B7d2oNMjtjUP2RhD-QnNDWNl3jHwjUDk5KLWQ2OnNQ4,58
|
|
7
7
|
primitive/agent/commands.py,sha256=-dVDilELfkGfbZB7qfEPs77Dm1oT62qJj4tsIk4KoxI,254
|
8
8
|
primitive/agent/process.py,sha256=LVI-RB4a0YEuXUTYMXKL5Xi9euNwUI2nxj00mv8EFOg,2253
|
9
9
|
primitive/agent/provision.py,sha256=rmwnro1K5F8mwtd45XAq7RVQmpDWnbBCQ8X_qgWhm3M,1546
|
10
|
-
primitive/agent/runner.py,sha256=
|
11
|
-
primitive/agent/uploader.py,sha256=
|
10
|
+
primitive/agent/runner.py,sha256=xt0Ty-dAc8VatNIZwBZUJbC3mHXLpywpRVVyr_RvVYw,7142
|
11
|
+
primitive/agent/uploader.py,sha256=W-aXUgKZvcm9LbTXq8su_cgBl_mFrmcFfkkU9t8W04Q,3002
|
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
|
@@ -20,13 +20,14 @@ primitive/daemons/commands.py,sha256=-Muh-6ib4uAVtPn_67AcMrDwuCwYlCnRQozCi2Xurmk
|
|
20
20
|
primitive/daemons/launch_agents.py,sha256=qovt32gwpjGDd82z_SY5EGCUjaUyNA49pZFajZsw3eE,4796
|
21
21
|
primitive/daemons/launch_service.py,sha256=FPB9qKEjhllRfEpct0ng2L9lpIaGJbQwn1JdFT8uBA8,5600
|
22
22
|
primitive/exec/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
23
|
-
primitive/exec/actions.py,sha256=
|
24
|
-
primitive/exec/commands.py,sha256=
|
23
|
+
primitive/exec/actions.py,sha256=Q7Nj4SVIh2XH83mirpBRQietwWZDZXdoBF3gGvUVzCU,3425
|
24
|
+
primitive/exec/commands.py,sha256=66LO2kkJC-ynNZQpUCXv4Ol15QoacdSZAHblePDcmLo,510
|
25
|
+
primitive/exec/interactive.py,sha256=TscY6s2ZysijidKPheq6y-fCErUVLS0zcdTW8XyFWGI,2435
|
25
26
|
primitive/files/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
26
|
-
primitive/files/actions.py,sha256=
|
27
|
-
primitive/files/commands.py,sha256=
|
27
|
+
primitive/files/actions.py,sha256=q33aP7UvCFfhhJ8iOnvI57jOpobBUaJspRN4_3vJApU,6675
|
28
|
+
primitive/files/commands.py,sha256=x1fxixMrZFvYZGeQb3u5ElsbmWXMmYGq0f_zZArGp8Q,1084
|
28
29
|
primitive/files/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
29
|
-
primitive/files/graphql/mutations.py,sha256=
|
30
|
+
primitive/files/graphql/mutations.py,sha256=SWxq6rwVWhouiuC72--Avpg9vybURFxmxiwkMY6dX7E,642
|
30
31
|
primitive/git/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
31
32
|
primitive/git/actions.py,sha256=0KHeHViZZqIhF6-Eqvhs0g_UmglqyWrOQKElQCm6jVw,1506
|
32
33
|
primitive/git/commands.py,sha256=sCeSjkRgSEjCEsB5seXgB_h6xfk0KpvMvzMKoRfUbRA,1177
|
@@ -37,12 +38,12 @@ primitive/graphql/relay.py,sha256=bmij2AjdpURQ6GGVCxwWhauF-r_SxuAU2oJ4sDbLxpI,72
|
|
37
38
|
primitive/graphql/sdk.py,sha256=BhCGmDtc4sNnH8CxbQSJyFwOZ-ZSqMtjsxMB3JRBhPw,1456
|
38
39
|
primitive/graphql/utility_fragments.py,sha256=uIjwILC4QtWNyO5vu77VjQf_p0jvP3A9q_6zRq91zqs,303
|
39
40
|
primitive/hardware/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
40
|
-
primitive/hardware/actions.py,sha256=
|
41
|
+
primitive/hardware/actions.py,sha256=fgwTASzElwk4PxLS2_sdIQYdwbRnuKczThV-aN_MT5A,19049
|
41
42
|
primitive/hardware/commands.py,sha256=_HaWOdRQSkhnA1xZZHZWgadSQ9Gijxtnzg2vc_IDSMA,1854
|
42
43
|
primitive/hardware/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
43
44
|
primitive/hardware/graphql/fragments.py,sha256=2uI0_WIhJISgd9Yx8tAdM7EUuAXEeQMUIXTIfkbZc2Q,246
|
44
45
|
primitive/hardware/graphql/mutations.py,sha256=Zd6HxnIgTJ9mJQAfKJkdeDfstcPAal6Bj38pnKb_RuI,904
|
45
|
-
primitive/hardware/graphql/queries.py,sha256=
|
46
|
+
primitive/hardware/graphql/queries.py,sha256=1xEe8JoXt_MmqJ3bIeCk13PpzyNFJn4OgRDU2_93mZA,574
|
46
47
|
primitive/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
47
48
|
primitive/jobs/actions.py,sha256=CtyO-Z9614TgIoXJJX1QGsoll0fgpBIjG9PJH5JwCQs,4901
|
48
49
|
primitive/jobs/commands.py,sha256=MxPCkBEYW_eLNqgCRYeyj7ZcLOFAWfpVZlqDR2Y_S0o,830
|
@@ -75,7 +76,7 @@ primitive/reservations/graphql/fragments.py,sha256=OPh8ylJR2kxfArBJ4IYIBLvWJyms3
|
|
75
76
|
primitive/reservations/graphql/mutations.py,sha256=IqzwQL7OclN7RpIcidrTQo9cGYofY7wqoBOdnY0pwN8,651
|
76
77
|
primitive/reservations/graphql/queries.py,sha256=x31wTRelskX2fc0fx2qrY7XT1q74nvzLv_Xef3o9weg,746
|
77
78
|
primitive/sim/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
78
|
-
primitive/sim/actions.py,sha256=
|
79
|
+
primitive/sim/actions.py,sha256=oR77UmCp6PxDEuKvoNejeHOG6E5r6uHax3G9OZYoofM,4810
|
79
80
|
primitive/sim/commands.py,sha256=8PaOfL1MO6qxTn7mNVRnBU1X2wa3gk_mlbAhBW6MnI0,591
|
80
81
|
primitive/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
81
82
|
primitive/utils/actions.py,sha256=HOFrmM3-0A_A3NS84MqrZ6JmQEiiPSoDqEeuu6b_qfQ,196
|
@@ -87,9 +88,9 @@ primitive/utils/git.py,sha256=1qNOu8X-33CavmrD580BmrFhD_WVO9PGWHUUboXJR_g,663
|
|
87
88
|
primitive/utils/memory_size.py,sha256=4xfha21kW82nFvOTtDFx9Jk2ZQoEhkfXii-PGNTpIUk,3058
|
88
89
|
primitive/utils/printer.py,sha256=f1XUpqi5dkTL3GWvYRUGlSwtj2IxU1q745T4Fxo7Tn4,370
|
89
90
|
primitive/utils/shell.py,sha256=j7E1YwgNWw57dFHVfEbqRNVcPHX0xDefX2vFSNgeI_8,1648
|
90
|
-
primitive/utils/verible.py,sha256=
|
91
|
-
primitive-0.1.
|
92
|
-
primitive-0.1.
|
93
|
-
primitive-0.1.
|
94
|
-
primitive-0.1.
|
95
|
-
primitive-0.1.
|
91
|
+
primitive/utils/verible.py,sha256=Zb5NUISvcaIgEvgCDBWr-GCoceMa79Tcwvr5Wl9lfnA,2252
|
92
|
+
primitive-0.1.62.dist-info/METADATA,sha256=sQ2iai9V1lS1U_snKtzwP4JsUgUaHPYyqzoRnmaHVhk,3811
|
93
|
+
primitive-0.1.62.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
94
|
+
primitive-0.1.62.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
|
95
|
+
primitive-0.1.62.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
|
96
|
+
primitive-0.1.62.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|