primitive 0.1.4__py3-none-any.whl → 0.1.6__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 CHANGED
@@ -1,4 +1,4 @@
1
1
  # SPDX-FileCopyrightText: 2024-present Dylan Stein <dylan@steins.studio>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "0.1.4"
4
+ __version__ = "0.1.6"
@@ -0,0 +1,59 @@
1
+ from time import sleep
2
+ from primitive.utils.actions import BaseAction
3
+ from loguru import logger
4
+
5
+
6
+ class Agent(BaseAction):
7
+ def execute(
8
+ self,
9
+ ):
10
+ logger.enable("primitive")
11
+ logger.debug("Starting Primitive Agent")
12
+ while True:
13
+ hardware = self.primitive.hardware.get_own_hardware_details()
14
+
15
+ active_reservation_id = None
16
+ if hardware.get("activeReservation"):
17
+ active_reservation_id = hardware["activeReservation"]["id"]
18
+ if not active_reservation_id:
19
+ logger.debug("No active reservation found")
20
+ sleep(5)
21
+ continue
22
+
23
+ job_runs_data = self.primitive.projects.get_job_runs(
24
+ status="pending", first=1, reservation_id=active_reservation_id
25
+ )
26
+
27
+ pending_job_runs = [
28
+ edge["node"] for edge in job_runs_data["jobRuns"]["edges"]
29
+ ]
30
+
31
+ for job_run in pending_job_runs:
32
+ logger.debug("Found pending Job Run")
33
+ logger.debug(f"Job Run ID: {job_run['id']}")
34
+ logger.debug(f"Job Name: {job_run['job']['name']}")
35
+
36
+ if job_run["job"]["slug"] == "lint":
37
+ logger.debug("Executing Lint Job")
38
+
39
+ self.primitive.projects.job_run_update(
40
+ job_run["id"], status="request_completed"
41
+ )
42
+
43
+ result, message = self.primitive.lint.execute()
44
+ if result:
45
+ self.primitive.projects.job_run_update(
46
+ job_run["id"],
47
+ status="request_completed",
48
+ conclusion="success",
49
+ stdout=message,
50
+ )
51
+ else:
52
+ self.primitive.projects.job_run_update(
53
+ job_run["id"],
54
+ status="request_completed",
55
+ conclusion="failure",
56
+ stdout=message,
57
+ )
58
+
59
+ sleep(5)
@@ -0,0 +1,13 @@
1
+ import click
2
+ import typing
3
+
4
+ if typing.TYPE_CHECKING:
5
+ from ..client import Primitive
6
+
7
+
8
+ @click.command("agent")
9
+ @click.pass_context
10
+ def cli(context):
11
+ """agent"""
12
+ primitive: Primitive = context.obj.get("PRIMITIVE")
13
+ primitive.agent.execute()
primitive/cli.py CHANGED
@@ -7,6 +7,7 @@ from .auth.commands import config_command, whoami_command
7
7
  from .files.commands import cli as file_commands
8
8
  from .hardware.commands import cli as hardware_commands
9
9
  from .lint.commands import cli as lint_commands
10
+ from .agent.commands import cli as agent_commands
10
11
 
11
12
 
12
13
  @click.group()
@@ -55,6 +56,7 @@ cli.add_command(whoami_command, "whoami")
55
56
  cli.add_command(file_commands, "files")
56
57
  cli.add_command(hardware_commands, "hardware")
57
58
  cli.add_command(lint_commands, "lint")
59
+ cli.add_command(agent_commands, "agent")
58
60
 
59
61
  if __name__ == "__main__":
60
62
  cli(obj={})
primitive/client.py CHANGED
@@ -7,6 +7,7 @@ from .files.actions import Files
7
7
  from .simulations.actions import Simulations
8
8
  from .hardware.actions import Hardware
9
9
  from .lint.actions import Lint
10
+ from .agent.actions import Agent
10
11
 
11
12
  from loguru import logger
12
13
 
@@ -57,6 +58,7 @@ class Primitive:
57
58
  self.simulations: Simulations = Simulations(self)
58
59
  self.hardware: Hardware = Hardware(self)
59
60
  self.lint: Lint = Lint(self)
61
+ self.agent: Agent = Agent(self)
60
62
 
61
63
  def get_host_config(self):
62
64
  self.full_config = read_config_file()
@@ -4,7 +4,7 @@ import json
4
4
  import platform
5
5
  from shutil import which
6
6
  import subprocess
7
- from typing import Dict, List
7
+ from typing import Dict, List, Optional
8
8
  import click
9
9
  from loguru import logger
10
10
  from primitive.utils.memory_size import MemorySize
@@ -445,3 +445,67 @@ class Hardware(BaseAction):
445
445
  message = " [*] Failed to check in! "
446
446
  logger.error(message)
447
447
  raise exception
448
+
449
+ def get_hardware_list(self, fingerprint: Optional[str] = None):
450
+ query = gql(
451
+ """
452
+ fragment HardwareFragment on Hardware {
453
+ id
454
+ pk
455
+ name
456
+ slug
457
+ createdAt
458
+ updatedAt
459
+ capabilities {
460
+ id
461
+ pk
462
+ }
463
+ activeReservation {
464
+ id
465
+ pk
466
+ }
467
+ }
468
+
469
+ query hardwareList(
470
+ $before: String
471
+ $after: String
472
+ $first: Int
473
+ $last: Int
474
+ $filters: HardwareFilters
475
+ ) {
476
+ hardwareList(
477
+ before: $before
478
+ after: $after
479
+ first: $first
480
+ last: $last
481
+ filters: $filters
482
+ ) {
483
+ totalCount
484
+ edges {
485
+ cursor
486
+ node {
487
+ ...HardwareFragment
488
+ }
489
+ }
490
+ }
491
+ }
492
+ """
493
+ )
494
+
495
+ filters = {}
496
+ if fingerprint:
497
+ filters["fingerprint"] = {"exact": fingerprint}
498
+
499
+ variables = {
500
+ "first": 1,
501
+ "filters": filters,
502
+ }
503
+ result = self.primitive.session.execute(query, variable_values=variables)
504
+ return result
505
+
506
+ def get_own_hardware_details(self):
507
+ hardware_list_data = self.get_hardware_list(
508
+ fingerprint=self.primitive.host_config.get("fingerprint")
509
+ )
510
+ hardware = hardware_list_data.get("hardwareList").get("edges")[0].get("node")
511
+ return hardware
@@ -6,6 +6,106 @@ from primitive.utils.actions import BaseAction
6
6
 
7
7
 
8
8
  class Projects(BaseAction):
9
+ def get_job_runs(
10
+ self,
11
+ organization_id: Optional[str] = None,
12
+ project_id: Optional[str] = None,
13
+ job_id: Optional[str] = None,
14
+ reservation_id: Optional[str] = None,
15
+ git_commit_id: Optional[str] = None,
16
+ status: Optional[str] = None,
17
+ conclusion: Optional[str] = None,
18
+ first: Optional[int] = 1,
19
+ last: Optional[int] = None,
20
+ ):
21
+ query = gql(
22
+ """
23
+ fragment PageInfoFragment on PageInfo {
24
+ hasNextPage
25
+ hasPreviousPage
26
+ startCursor
27
+ endCursor
28
+ }
29
+
30
+ fragment JobRunFragment on JobRun {
31
+ id
32
+ pk
33
+ createdAt
34
+ updatedAt
35
+ completedAt
36
+ startedAt
37
+ status
38
+ conclusion
39
+ stdout
40
+ job {
41
+ id
42
+ pk
43
+ slug
44
+ name
45
+ createdAt
46
+ updatedAt
47
+ }
48
+ }
49
+
50
+ query jobRuns(
51
+ $before: String
52
+ $after: String
53
+ $first: Int
54
+ $last: Int
55
+ $filters: JobRunFilters
56
+ $order: JobRunOrder
57
+ ) {
58
+ jobRuns(
59
+ before: $before
60
+ after: $after
61
+ first: $first
62
+ last: $last
63
+ filters: $filters
64
+ order: $order
65
+ ) {
66
+ totalCount
67
+ pageInfo {
68
+ ...PageInfoFragment
69
+ }
70
+ edges {
71
+ cursor
72
+ node {
73
+ ...JobRunFragment
74
+ }
75
+ }
76
+ }
77
+ }
78
+ """
79
+ )
80
+
81
+ filters = {}
82
+ if organization_id:
83
+ filters["organization"] = {"id": organization_id}
84
+ if project_id:
85
+ filters["project"] = {"id": project_id}
86
+ if job_id:
87
+ filters["job"] = {"id": job_id}
88
+ if reservation_id:
89
+ filters["reservation"] = {"id": reservation_id}
90
+ if git_commit_id:
91
+ filters["gitCommit"] = {"id": git_commit_id}
92
+ if status:
93
+ filters["status"] = {"exact": status}
94
+ if conclusion:
95
+ filters["conclusion"] = {"exact": status}
96
+
97
+ variables = {
98
+ "first": first,
99
+ "last": last,
100
+ "filters": filters,
101
+ "order": {
102
+ "createdAt": "DESC",
103
+ },
104
+ }
105
+
106
+ result = self.primitive.session.execute(query, variable_values=variables)
107
+ return result
108
+
9
109
  def get_job_run(self, id: str):
10
110
  query = gql(
11
111
  """
@@ -28,6 +128,7 @@ class Projects(BaseAction):
28
128
  id: str,
29
129
  status: str = None,
30
130
  conclusion: str = None,
131
+ stdout: str = None,
31
132
  file_ids: Optional[List[str]] = [],
32
133
  ):
33
134
  mutation = gql(
@@ -50,6 +151,8 @@ class Projects(BaseAction):
50
151
  input["conclusion"] = conclusion
51
152
  if file_ids and len(file_ids) > 0:
52
153
  input["files"] = file_ids
154
+ if stdout:
155
+ input["stdout"] = stdout
53
156
  variables = {"input": input}
54
157
  result = self.primitive.session.execute(mutation, variable_values=variables)
55
158
  return result
primitive/utils/shell.py CHANGED
@@ -3,61 +3,31 @@ import subprocess
3
3
 
4
4
 
5
5
  def add_path_to_shell(path: Path):
6
+ if not path.exists():
7
+ raise Exception(f"{path} does not exist.")
8
+
6
9
  try:
7
10
  subprocess.run(["fish_add_path", path], capture_output=True)
8
11
  return True
9
12
  except FileNotFoundError:
10
13
  pass
11
14
 
12
- if Path.home() / ".bash_profile":
13
- subprocess.run(
14
- [
15
- "echo",
16
- f"'export PATH={path}:$PATH'",
17
- ">>",
18
- "~/.bash_profile",
19
- ],
20
- capture_output=True,
21
- )
22
- elif Path.home() / ".bashrc":
23
- subprocess.run(
24
- [
25
- "echo",
26
- f"'export PATH={path}:$PATH'",
27
- ">>",
28
- "~/.bashrc",
29
- ],
30
- capture_output=True,
31
- )
32
- elif Path.home() / ".zshrc":
33
- subprocess.run(
34
- [
35
- "echo",
36
- f"'export PATH={path}:$PATH'",
37
- ">>",
38
- "~/.zshrc",
39
- ],
40
- capture_output=True,
41
- )
42
- elif Path.home() / ".profile":
43
- subprocess.run(
44
- [
45
- "echo",
46
- f"'export PATH={path}:$PATH'",
47
- ">>",
48
- "~/.profile",
49
- ],
50
- capture_output=True,
51
- )
52
- elif Path.home() / ".bash_login":
53
- subprocess.run(
54
- [
55
- "echo",
56
- f"'export PATH={path}:$PATH'",
57
- ">>",
58
- "~/.bash_login",
59
- ],
60
- capture_output=True,
61
- )
15
+ profile_path = None
16
+
17
+ if Path.home().joinpath(".bash_profile").exists():
18
+ profile_path = Path.home().joinpath(".bash_profile")
19
+ elif Path.home().joinpath(".bashrc").exists():
20
+ profile_path = Path.home().joinpath(".bashrc")
21
+ elif Path.home().joinpath(".zshrc").exists():
22
+ profile_path = Path.home().joinpath(".zshrc")
23
+ elif Path.home().joinpath(".profile").exists():
24
+ profile_path = Path.home().joinpath(".profile")
25
+ elif Path.home().joinpath(".bash_login").exists():
26
+ profile_path = Path.home().joinpath(".bash_login")
62
27
  else:
63
28
  raise Exception(f"Failed to add {path} to PATH")
29
+
30
+ with open(profile_path, "a") as file:
31
+ file.write(f"export PATH={path}:$PATH\n")
32
+
33
+ return True
@@ -18,9 +18,9 @@ def install_verible(system_info: dict) -> bool:
18
18
  url = VERIBLE_MAC_OS_LINK
19
19
  elif system_info.get("os_family") == "Windows":
20
20
  url = VERIBLE_WINDOWS_64_OS_LINK
21
- elif system_info.processor == "x86_64":
21
+ elif system_info.get("processor") == "x86_64":
22
22
  url = VERIBLE_LINUX_X86_64_OS_LINK
23
- elif system_info.processor == "arm":
23
+ elif system_info.get("processor") == "arm":
24
24
  url = VERIBLE_LINUX_X86_64_OS_LINK
25
25
 
26
26
  verible_dir_name = url.split("/")[-1].split(".tar.gz")[0]
@@ -43,7 +43,11 @@ def install_verible(system_info: dict) -> bool:
43
43
  logger.debug("Deleting tar.gz artifact")
44
44
  file_download_path.unlink()
45
45
 
46
- verible_bin = Path.home() / verible_dir_name / "bin"
46
+ unpacked_verible_dir_name = verible_dir_name
47
+ if "linux" in unpacked_verible_dir_name:
48
+ unpacked_verible_dir_name = unpacked_verible_dir_name.split("-linux")[0]
49
+
50
+ verible_bin = Path.home().joinpath(unpacked_verible_dir_name).joinpath("bin")
47
51
 
48
52
  logger.debug("Adding verible to PATH")
49
53
  add_path_to_shell(verible_bin)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: primitive
3
- Version: 0.1.4
3
+ Version: 0.1.6
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
@@ -36,6 +36,7 @@ Description-Content-Type: text/markdown
36
36
  **Table of Contents**
37
37
 
38
38
  - [Installation](#installation)
39
+ - [Configuration](#configuration)
39
40
  - [License](#license)
40
41
 
41
42
  ## Installation
@@ -44,6 +45,20 @@ Description-Content-Type: text/markdown
44
45
  pip install primitive
45
46
  ```
46
47
 
48
+ ## Configuration
49
+
50
+ ### Authenticate
51
+
52
+ ```console
53
+ primitive config
54
+ ```
55
+
56
+ ### Register your Hardware
57
+
58
+ ```console
59
+ primitive hardware register
60
+ ```
61
+
47
62
  ## License
48
63
 
49
64
  `primitive` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
@@ -1,7 +1,9 @@
1
- primitive/__about__.py,sha256=E2SXPkbuFZByKB4zIeOf_Me_bVBs-mUA8TpF7fO1FrM,128
1
+ primitive/__about__.py,sha256=P5wASxX77-g_YRSjb4MnBDu4CWjITURFbU8iN8R3rlM,128
2
2
  primitive/__init__.py,sha256=bwKdgggKNVssJFVPfKSxqFMz4IxSr54WWbmiZqTMPNI,106
3
- primitive/cli.py,sha256=QC4jbu8s0uhNxFmaTO2B-X1o1gpyFX3xJVHU-j5CvQE,1705
4
- primitive/client.py,sha256=cZiJjzZ4HkCtYViDNOIoNuf7mcp772eSCSztsqh381E,2010
3
+ primitive/cli.py,sha256=LXSpPtqQLxkOXsnsbs4UN3n7G_pHCz8AkBFCrk7Is4g,1796
4
+ primitive/client.py,sha256=3RMRNY4ZiGT2OKnZtquWyrXjRSgDe5HlZvtFNKZErLQ,2083
5
+ primitive/agent/actions.py,sha256=Fq6GEJqIwv-hWABFOl3YC8l0gBNME3byycNYqICpZG8,2144
6
+ primitive/agent/commands.py,sha256=-dVDilELfkGfbZB7qfEPs77Dm1oT62qJj4tsIk4KoxI,254
5
7
  primitive/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
8
  primitive/auth/actions.py,sha256=N2bGcwXNsB89pzs66gF9A5_WzUScY5fhfOyWixqo2y8,1054
7
9
  primitive/auth/commands.py,sha256=JahUq0E2e7Xa-FX1WEUv7TgM6ieDvNH4VwRRtxAW7HE,2340
@@ -9,12 +11,12 @@ primitive/files/actions.py,sha256=f4JN3QFB2WXw-0JpnE-4-movnqtvXIpCrGd_9pdkeW4,26
9
11
  primitive/files/commands.py,sha256=DDizo3xJnU3KLUBTMeeM72viVpnJinLwxs84tmqKhqo,810
10
12
  primitive/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
13
  primitive/graphql/sdk.py,sha256=BhCGmDtc4sNnH8CxbQSJyFwOZ-ZSqMtjsxMB3JRBhPw,1456
12
- primitive/hardware/actions.py,sha256=BbFz17NKVDiI-9SiV1R1GPqsXRZwUm9oNj-Dc_bBFJ8,17133
14
+ primitive/hardware/actions.py,sha256=UL2Wyh_XFXKVNEvlVCe1ltCZHpNaRSXPNwSR8hGJyR0,18343
13
15
  primitive/hardware/commands.py,sha256=QE7LLeFdfOqlvz3JwdwJJRZAY3fHI1zB9kYmmDajpq0,1477
14
16
  primitive/lint/actions.py,sha256=FVigksFrADPDD3zHS0g0riyVrge7eEtwWk5PG2aZv9A,2352
15
17
  primitive/lint/commands.py,sha256=3CZvkOEMpJspJWmaQzA5bpPKx0_VCijQIXA9l-eTnZE,487
16
18
  primitive/projects/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- primitive/projects/actions.py,sha256=Zi0uFCUG5KU7w7HNE8eGlhUPdsJyjVrakcdU1PR8-MQ,1481
19
+ primitive/projects/actions.py,sha256=h1rgt3tWUPye0gnFvIZzZz7ibTbxyBS4q9RryjkVLzY,3600
18
20
  primitive/simulations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
21
  primitive/simulations/actions.py,sha256=YR0oxxd7_kuUIH77BWZLUs9rLRiSJztPPhpgDJU2PbY,1267
20
22
  primitive/utils/actions.py,sha256=HOFrmM3-0A_A3NS84MqrZ6JmQEiiPSoDqEeuu6b_qfQ,196
@@ -23,10 +25,10 @@ primitive/utils/files.py,sha256=Ug5UqrS9eZoTd08EFtgyyp8flsJTfsG9CwXBWmI-yFA,606
23
25
  primitive/utils/git.py,sha256=1qNOu8X-33CavmrD580BmrFhD_WVO9PGWHUUboXJR_g,663
24
26
  primitive/utils/memory_size.py,sha256=4xfha21kW82nFvOTtDFx9Jk2ZQoEhkfXii-PGNTpIUk,3058
25
27
  primitive/utils/printer.py,sha256=f1XUpqi5dkTL3GWvYRUGlSwtj2IxU1q745T4Fxo7Tn4,370
26
- primitive/utils/shell.py,sha256=xGsz3-3mmUP5xRrFotNj0UpbHmRSsiliQnyPzB48xLI,1567
27
- primitive/utils/verible.py,sha256=o2NBbqteePgFY-28S7AaEivSEszZcePFkMRXpg_XhHg,1990
28
- primitive-0.1.4.dist-info/METADATA,sha256=bW4QfYYNW6TWBazUnmjK3s7see0nYczt4UHcyupynYw,1642
29
- primitive-0.1.4.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
30
- primitive-0.1.4.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
31
- primitive-0.1.4.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
32
- primitive-0.1.4.dist-info/RECORD,,
28
+ primitive/utils/shell.py,sha256=-7UjQaBqSGHzEEyX8pNjeYFFP0P3lVnDV0OkgPz1qHU,1050
29
+ primitive/utils/verible.py,sha256=QYczN1IvxODfj4jeq0nqjFuF0Oi0Zdx-Q32ySOJgcw8,2205
30
+ primitive-0.1.6.dist-info/METADATA,sha256=k2nIZBpBfzNRizaA-5a9BW7qtOGmdQTeNF2lOdvocPU,1817
31
+ primitive-0.1.6.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
32
+ primitive-0.1.6.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
33
+ primitive-0.1.6.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
34
+ primitive-0.1.6.dist-info/RECORD,,