primitive 0.1.14__tar.gz → 0.1.16__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.
- {primitive-0.1.14 → primitive-0.1.16}/PKG-INFO +1 -1
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/__about__.py +1 -1
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/agent/actions.py +53 -22
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/cli.py +2 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/client.py +2 -2
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/daemons/launch_agents.py +11 -1
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/daemons/launch_service.py +10 -1
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/jobs/actions.py +4 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/lint/actions.py +1 -1
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/projects/actions.py +0 -1
- primitive-0.1.16/src/primitive/sim/actions.py +190 -0
- primitive-0.1.16/src/primitive/sim/commands.py +19 -0
- primitive-0.1.16/src/primitive/sim/vcd.py +761 -0
- primitive-0.1.14/src/primitive/simulations/actions.py +0 -48
- {primitive-0.1.14 → primitive-0.1.16}/.git-hooks/pre-commit +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/.gitattributes +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/.github/workflows/lint.yml +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/.github/workflows/publish.yml +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/.gitignore +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/.vscode/settings.json +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/LICENSE.txt +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/Makefile +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/README.md +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/linux setup.md +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/pyproject.toml +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/requirements.txt +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/__init__.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/agent/commands.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/auth/__init__.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/auth/actions.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/auth/commands.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/daemons/actions.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/daemons/commands.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/files/actions.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/files/commands.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/git/__init__.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/git/actions.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/git/commands.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/graphql/__init__.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/graphql/sdk.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/hardware/actions.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/hardware/commands.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/jobs/commands.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/lint/commands.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/organizations/actions.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/organizations/commands.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/projects/__init__.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/projects/commands.py +0 -0
- {primitive-0.1.14/src/primitive/simulations → primitive-0.1.16/src/primitive/sim}/__init__.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/utils/actions.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/utils/config.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/utils/files.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/utils/git.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/utils/memory_size.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/utils/printer.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/utils/shell.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/src/primitive/utils/verible.py +0 -0
- {primitive-0.1.14 → primitive-0.1.16}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: primitive
|
3
|
-
Version: 0.1.
|
3
|
+
Version: 0.1.16
|
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
|
@@ -65,28 +65,59 @@ class Agent(BaseAction):
|
|
65
65
|
)
|
66
66
|
)
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
68
|
+
match job_run["job"]["slug"]:
|
69
|
+
case "lint":
|
70
|
+
logger.debug("Executing Lint Job")
|
71
|
+
|
72
|
+
self.primitive.jobs.job_run_update(
|
73
|
+
job_run["id"], status="request_in_progress"
|
74
|
+
)
|
75
|
+
|
76
|
+
result, message = self.primitive.lint.execute(
|
77
|
+
source=downloaded_git_repository_dir
|
78
|
+
)
|
79
|
+
if result:
|
80
|
+
conclusion = "success"
|
81
|
+
else:
|
82
|
+
conclusion = "failure"
|
83
|
+
self.primitive.jobs.job_run_update(
|
84
|
+
job_run["id"],
|
85
|
+
status="request_completed",
|
86
|
+
conclusion=conclusion,
|
87
|
+
stdout=message,
|
88
|
+
)
|
89
|
+
|
90
|
+
logger.debug("Lint Job Completed")
|
91
|
+
case "sim":
|
92
|
+
logger.debug("Executing Sim Job")
|
93
|
+
|
94
|
+
self.primitive.job.job_run_update(
|
95
|
+
job_run["id"], status="request_in_progress"
|
96
|
+
)
|
97
|
+
|
98
|
+
result, message = self.primitive.sim.execute(
|
99
|
+
source=downloaded_git_repository_dir,
|
100
|
+
cmd=(
|
101
|
+
"make",
|
102
|
+
"all",
|
103
|
+
), # TODO: Change this to use container args container cmd
|
104
|
+
)
|
105
|
+
|
106
|
+
# Attempt artifact collection
|
107
|
+
self.primitive.sim.collect_artifacts(
|
108
|
+
source=downloaded_git_repository_dir
|
109
|
+
)
|
110
|
+
|
111
|
+
if result:
|
112
|
+
conclusion = "success"
|
113
|
+
else:
|
114
|
+
conclusion = "failure"
|
115
|
+
self.primitive.jobs.job_run_update(
|
116
|
+
job_run["id"],
|
117
|
+
status="request_completed",
|
118
|
+
conclusion=conclusion,
|
119
|
+
stdout=message,
|
120
|
+
)
|
90
121
|
|
91
122
|
sleep(5)
|
92
123
|
except KeyboardInterrupt:
|
@@ -13,6 +13,7 @@ from .daemons.commands import cli as daemons_commands
|
|
13
13
|
from .jobs.commands import cli as jobs_commands
|
14
14
|
from .organizations.commands import cli as organizations_commands
|
15
15
|
from .projects.commands import cli as projects_commands
|
16
|
+
from .sim.commands import cli as sim_commands
|
16
17
|
|
17
18
|
|
18
19
|
@click.group()
|
@@ -67,6 +68,7 @@ cli.add_command(daemons_commands, "daemons")
|
|
67
68
|
cli.add_command(jobs_commands, "jobs")
|
68
69
|
cli.add_command(organizations_commands, "organizations")
|
69
70
|
cli.add_command(projects_commands, "projects")
|
71
|
+
cli.add_command(sim_commands, "sim")
|
70
72
|
|
71
73
|
if __name__ == "__main__":
|
72
74
|
cli(obj={})
|
@@ -4,7 +4,7 @@ from .projects.actions import Projects
|
|
4
4
|
from .graphql.sdk import create_session
|
5
5
|
from .utils.config import read_config_file
|
6
6
|
from .files.actions import Files
|
7
|
-
from .
|
7
|
+
from .sim.actions import Sim
|
8
8
|
from .hardware.actions import Hardware
|
9
9
|
from .lint.actions import Lint
|
10
10
|
from .agent.actions import Agent
|
@@ -61,7 +61,7 @@ class Primitive:
|
|
61
61
|
self.projects: Projects = Projects(self)
|
62
62
|
self.jobs: Jobs = Jobs(self)
|
63
63
|
self.files: Files = Files(self)
|
64
|
-
self.
|
64
|
+
self.sim: Sim = Sim(self)
|
65
65
|
self.hardware: Hardware = Hardware(self)
|
66
66
|
self.lint: Lint = Lint(self)
|
67
67
|
self.agent: Agent = Agent(self)
|
@@ -6,6 +6,7 @@ HOME_DIRECTORY = Path.home()
|
|
6
6
|
CURRENT_USER = str(HOME_DIRECTORY.expanduser()).lstrip("/Users/")
|
7
7
|
|
8
8
|
PRIMITIVE_BINARY_PATH = Path(HOME_DIRECTORY / ".pyenv" / "shims" / "primitive")
|
9
|
+
|
9
10
|
PRIMITIVE_LAUNCH_AGENT_FILEPATH = Path(
|
10
11
|
HOME_DIRECTORY / "Library" / "LaunchAgents" / "tech.primitive.agent.plist"
|
11
12
|
)
|
@@ -78,6 +79,15 @@ def populate_fresh_launch_agent():
|
|
78
79
|
PRIMITIVE_LAUNCH_AGENT_FILEPATH.parent.mkdir(parents=True, exist_ok=True)
|
79
80
|
PRIMITIVE_LAUNCH_AGENT_FILEPATH.touch()
|
80
81
|
|
82
|
+
found_primitive_binary_path = PRIMITIVE_BINARY_PATH
|
83
|
+
if not PRIMITIVE_BINARY_PATH.exists():
|
84
|
+
result = subprocess.run(["which", "primitive"], capture_output=True)
|
85
|
+
if result.returncode == 0:
|
86
|
+
found_primitive_binary_path = result.stdout.decode().rstrip("\n")
|
87
|
+
else:
|
88
|
+
print("primitive binary not found")
|
89
|
+
return False
|
90
|
+
|
81
91
|
PRIMITIVE_LAUNCH_AGENT_FILEPATH.write_text(
|
82
92
|
f"""<?xml version="1.0" encoding="UTF-8"?>
|
83
93
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
@@ -96,7 +106,7 @@ def populate_fresh_launch_agent():
|
|
96
106
|
</array>
|
97
107
|
<key>ProgramArguments</key>
|
98
108
|
<array>
|
99
|
-
<string>{
|
109
|
+
<string>{found_primitive_binary_path}</string>
|
100
110
|
<string>agent</string>
|
101
111
|
</array>
|
102
112
|
<key>RunAtLoad</key>
|
@@ -82,8 +82,17 @@ def populate_service_file():
|
|
82
82
|
"After": "network.target",
|
83
83
|
}
|
84
84
|
|
85
|
+
found_primitive_binary_path = PRIMITIVE_BINARY_PATH
|
86
|
+
if not PRIMITIVE_BINARY_PATH.exists():
|
87
|
+
result = subprocess.run(["which", "primitive"], capture_output=True)
|
88
|
+
if result.returncode == 0:
|
89
|
+
found_primitive_binary_path = result.stdout.decode().rstrip("\n")
|
90
|
+
else:
|
91
|
+
print("primitive binary not found")
|
92
|
+
return False
|
93
|
+
|
85
94
|
config["Service"] = {
|
86
|
-
"ExecStart": f"{
|
95
|
+
"ExecStart": f"{found_primitive_binary_path} agent",
|
87
96
|
"Restart": "always",
|
88
97
|
"StandardError": f"append:{PRIMITIVE_AGENT_LOGS_FILEPATH}",
|
89
98
|
"StandardOutput": f"append:{PRIMITIVE_AGENT_LOGS_FILEPATH}",
|
@@ -9,7 +9,7 @@ from ..utils.verible import install_verible
|
|
9
9
|
|
10
10
|
class Lint(BaseAction):
|
11
11
|
def execute(self, source: Path = Path.cwd()) -> Tuple[bool, str]:
|
12
|
-
logger.debug("Starting linter for source: {source}")
|
12
|
+
logger.debug(f"Starting linter for source: {source}")
|
13
13
|
files = find_files_for_extension(source, ".sv")
|
14
14
|
if not files:
|
15
15
|
message = "No files found to lint."
|
@@ -0,0 +1,190 @@
|
|
1
|
+
from gql import gql
|
2
|
+
from pathlib import Path
|
3
|
+
from primitive.utils.actions import BaseAction
|
4
|
+
from loguru import logger
|
5
|
+
import subprocess
|
6
|
+
from typing import Tuple, List
|
7
|
+
from ..utils.files import find_files_for_extension
|
8
|
+
import os
|
9
|
+
from .vcd import TokenKind, tokenize
|
10
|
+
import io
|
11
|
+
from collections import defaultdict
|
12
|
+
import urllib
|
13
|
+
import json
|
14
|
+
|
15
|
+
|
16
|
+
class Sim(BaseAction):
|
17
|
+
def execute(
|
18
|
+
self, source: Path = Path.cwd(), cmd: Tuple[str] = ["make"]
|
19
|
+
) -> Tuple[bool, str]:
|
20
|
+
logger.debug(f"Starting simulation run for source: {source}")
|
21
|
+
|
22
|
+
os.chdir(source)
|
23
|
+
logger.debug(f"Changed to {source}, starting sim run")
|
24
|
+
try:
|
25
|
+
result = subprocess.run(cmd, capture_output=True, text=True, env=os.environ)
|
26
|
+
except FileNotFoundError:
|
27
|
+
message = f"Did not find {cmd}"
|
28
|
+
logger.error(message)
|
29
|
+
return False, message
|
30
|
+
|
31
|
+
logger.debug("Sim run complete.")
|
32
|
+
|
33
|
+
message = ""
|
34
|
+
if result.stderr:
|
35
|
+
logger.error("\n" + result.stderr)
|
36
|
+
if result.stdout:
|
37
|
+
logger.info("\n" + result.stdout)
|
38
|
+
message = "See above logs for sim output."
|
39
|
+
|
40
|
+
if result.returncode != 0:
|
41
|
+
if not self.primitive.DEBUG:
|
42
|
+
message = result.stderr
|
43
|
+
return False, message
|
44
|
+
else:
|
45
|
+
message = "Sim run successful."
|
46
|
+
|
47
|
+
return True, message
|
48
|
+
|
49
|
+
def upload_file(self, path: Path) -> str:
|
50
|
+
file_upload_response = self.primitive.files.file_upload(
|
51
|
+
path, key_prefix=f"{self.job_run_id}/{str(path.parent)}"
|
52
|
+
)
|
53
|
+
return file_upload_response.json()["data"]["fileUpload"]["id"]
|
54
|
+
|
55
|
+
def collect_artifacts(self, source) -> None:
|
56
|
+
file_ids = []
|
57
|
+
|
58
|
+
# Look for VCD artifacts
|
59
|
+
files = find_files_for_extension(source, ".vcd")
|
60
|
+
for file in files:
|
61
|
+
trace_file_ids = self.generate_timeseries(path=file)
|
62
|
+
file_ids.extend(trace_file_ids)
|
63
|
+
|
64
|
+
logger.debug("Uploading additional artifacts...")
|
65
|
+
files = find_files_for_extension(source, (".xml", ".vcd", ".log", ".history"))
|
66
|
+
for file_path in files:
|
67
|
+
try:
|
68
|
+
file_ids.append(self.upload_file(file_path))
|
69
|
+
except FileNotFoundError:
|
70
|
+
logger.warning(f"{file_path} not found...")
|
71
|
+
|
72
|
+
logger.debug("Updating job run...")
|
73
|
+
if len(file_ids) > 0:
|
74
|
+
job_run_update_response = self.primitive.projects.job_run_update(
|
75
|
+
id=self.job_run_id, file_ids=file_ids
|
76
|
+
)
|
77
|
+
logger.success(job_run_update_response)
|
78
|
+
|
79
|
+
def generate_timeseries(self, path: Path) -> List[str]:
|
80
|
+
logger.debug("Parsing VCD file...")
|
81
|
+
with open(path, "rb") as f:
|
82
|
+
tokens = tokenize(io.BytesIO(f.read()))
|
83
|
+
|
84
|
+
metadata = defaultdict(dict)
|
85
|
+
traces = defaultdict(list)
|
86
|
+
timescale_unit = "s"
|
87
|
+
timescale_magnitude = 1
|
88
|
+
active_module: str = ""
|
89
|
+
time: int = 0
|
90
|
+
|
91
|
+
for token in tokens:
|
92
|
+
match token.kind:
|
93
|
+
case TokenKind.TIMESCALE:
|
94
|
+
timescale_unit = token.data.unit.value
|
95
|
+
timescale_magnitude = token.data.magnitude.value
|
96
|
+
case TokenKind.SCOPE:
|
97
|
+
active_module = token.data.ident
|
98
|
+
case TokenKind.CHANGE_TIME:
|
99
|
+
time = int(token.data)
|
100
|
+
case TokenKind.VAR:
|
101
|
+
var = {
|
102
|
+
"id_code": token.data.id_code,
|
103
|
+
"module": active_module,
|
104
|
+
"var_type": str(token.data.type_),
|
105
|
+
"var_size": token.data.size,
|
106
|
+
"reference": token.data.reference,
|
107
|
+
"bit_index": str(token.data.bit_index),
|
108
|
+
}
|
109
|
+
metadata[token.data.id_code] = var
|
110
|
+
case TokenKind.CHANGE_SCALAR:
|
111
|
+
traces[token.data.id_code].append(
|
112
|
+
(str(time), str(token.data.value))
|
113
|
+
)
|
114
|
+
case TokenKind.CHANGE_VECTOR:
|
115
|
+
traces[token.data.id_code].append(
|
116
|
+
(str(time), str(token.data.value))
|
117
|
+
)
|
118
|
+
|
119
|
+
# Add traces and write files
|
120
|
+
logger.debug("Uploading traces...")
|
121
|
+
trace_file_ids = []
|
122
|
+
for id_code, timeseries in traces.items():
|
123
|
+
|
124
|
+
def hashed(id_code):
|
125
|
+
return urllib.parse.quote_plus(id_code, safe="")
|
126
|
+
|
127
|
+
file_path = path.parent / f"{hashed(id_code)}.vcd.json"
|
128
|
+
with open(file_path, "w") as f:
|
129
|
+
f.write(json.dumps(timeseries))
|
130
|
+
|
131
|
+
trace_file_id = self.upload_file(file_path)
|
132
|
+
trace_file_ids.append(trace_file_id)
|
133
|
+
|
134
|
+
self.trace_create(
|
135
|
+
id_code=id_code,
|
136
|
+
module=metadata[id_code]["module"],
|
137
|
+
var_type=metadata[id_code]["var_type"],
|
138
|
+
var_size=metadata[id_code]["var_size"],
|
139
|
+
reference=metadata[id_code]["reference"],
|
140
|
+
bit_index=metadata[id_code]["bit_index"],
|
141
|
+
timescale_unit=timescale_unit,
|
142
|
+
timescale_magnitude=timescale_magnitude,
|
143
|
+
organization=self.organization_id,
|
144
|
+
file=trace_file_id,
|
145
|
+
job_run=self.job_run_id,
|
146
|
+
)
|
147
|
+
|
148
|
+
return trace_file_ids
|
149
|
+
|
150
|
+
def trace_create(
|
151
|
+
self,
|
152
|
+
id_code: str,
|
153
|
+
module: str,
|
154
|
+
var_type: str,
|
155
|
+
var_size: int,
|
156
|
+
reference: str,
|
157
|
+
bit_index: str,
|
158
|
+
timescale_unit: str,
|
159
|
+
timescale_magnitude: int,
|
160
|
+
organization: str,
|
161
|
+
file: str,
|
162
|
+
job_run: str,
|
163
|
+
):
|
164
|
+
mutation = gql(
|
165
|
+
"""
|
166
|
+
mutation createTrace($input: TraceCreateInput!) {
|
167
|
+
traceCreate(input: $input) {
|
168
|
+
... on Trace {
|
169
|
+
id
|
170
|
+
}
|
171
|
+
}
|
172
|
+
}
|
173
|
+
"""
|
174
|
+
)
|
175
|
+
input = {
|
176
|
+
"idCode": id_code,
|
177
|
+
"module": module,
|
178
|
+
"varType": var_type,
|
179
|
+
"varSize": var_size,
|
180
|
+
"reference": reference,
|
181
|
+
"bitIndex": bit_index,
|
182
|
+
"timescaleUnit": timescale_unit,
|
183
|
+
"timescaleMagnitude": timescale_magnitude,
|
184
|
+
"organization": organization,
|
185
|
+
"file": file,
|
186
|
+
"jobRun": job_run,
|
187
|
+
}
|
188
|
+
variables = {"input": input}
|
189
|
+
result = self.primitive.session.execute(mutation, variable_values=variables)
|
190
|
+
return result
|
@@ -0,0 +1,19 @@
|
|
1
|
+
import click
|
2
|
+
from pathlib import Path
|
3
|
+
import typing
|
4
|
+
from typing import Tuple
|
5
|
+
from ..utils.printer import print_result
|
6
|
+
|
7
|
+
if typing.TYPE_CHECKING:
|
8
|
+
from ..client import Primitive
|
9
|
+
|
10
|
+
|
11
|
+
@click.command("sim")
|
12
|
+
@click.pass_context
|
13
|
+
@click.argument("cmd", nargs=-1, required=True)
|
14
|
+
@click.option("--source", type=click.Path(exists=True), default=".")
|
15
|
+
def cli(context, source: str, cmd: Tuple[str]) -> None:
|
16
|
+
"""Sim"""
|
17
|
+
primitive: Primitive = context.obj.get("PRIMITIVE")
|
18
|
+
result, message = primitive.sim.execute(source=Path(source), cmd=cmd)
|
19
|
+
print_result(message=message, context=context)
|