primitive 0.1.11__py3-none-any.whl → 0.1.13__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.11"
4
+ __version__ = "0.1.13"
@@ -45,28 +45,48 @@ class Agent(BaseAction):
45
45
  logger.debug(f"Job Run ID: {job_run['id']}")
46
46
  logger.debug(f"Job Name: {job_run['job']['name']}")
47
47
 
48
+ git_repo_full_name = job_run["gitCommit"]["repoFullName"]
49
+ git_ref = job_run["gitCommit"]["sha"]
50
+ logger.debug(
51
+ f"Downloading repository {git_repo_full_name} at ref {git_ref}"
52
+ )
53
+
54
+ github_access_token = (
55
+ self.primitive.projects.github_access_token_for_job_run(
56
+ job_run["id"]
57
+ )
58
+ )
59
+
60
+ downloaded_git_repository_dir = (
61
+ self.primitive.git.download_git_repository_at_ref(
62
+ git_repo_full_name=git_repo_full_name,
63
+ git_ref=git_ref,
64
+ github_access_token=github_access_token,
65
+ )
66
+ )
67
+
48
68
  if job_run["job"]["slug"] == "lint":
49
69
  logger.debug("Executing Lint Job")
50
70
 
51
71
  self.primitive.projects.job_run_update(
52
- job_run["id"], status="request_completed"
72
+ job_run["id"], status="request_in_progress"
53
73
  )
54
74
 
55
- result, message = self.primitive.lint.execute()
75
+ result, message = self.primitive.lint.execute(
76
+ source=downloaded_git_repository_dir
77
+ )
56
78
  if result:
57
- self.primitive.projects.job_run_update(
58
- job_run["id"],
59
- status="request_completed",
60
- conclusion="success",
61
- stdout=message,
62
- )
79
+ conclusion = "success"
63
80
  else:
64
- self.primitive.projects.job_run_update(
65
- job_run["id"],
66
- status="request_completed",
67
- conclusion="failure",
68
- stdout=message,
69
- )
81
+ conclusion = "failure"
82
+ self.primitive.projects.job_run_update(
83
+ job_run["id"],
84
+ status="request_completed",
85
+ conclusion=conclusion,
86
+ stdout=message,
87
+ )
88
+
89
+ logger.debug("Lint Job Completed")
70
90
 
71
91
  sleep(5)
72
92
  except KeyboardInterrupt:
primitive/cli.py CHANGED
@@ -8,6 +8,8 @@ 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
10
  from .agent.commands import cli as agent_commands
11
+ from .git.commands import cli as git_commands
12
+ from .daemons.commands import cli as daemons_commands
11
13
 
12
14
 
13
15
  @click.group()
@@ -57,6 +59,8 @@ cli.add_command(file_commands, "files")
57
59
  cli.add_command(hardware_commands, "hardware")
58
60
  cli.add_command(lint_commands, "lint")
59
61
  cli.add_command(agent_commands, "agent")
62
+ cli.add_command(git_commands, "git")
63
+ cli.add_command(daemons_commands, "daemons")
60
64
 
61
65
  if __name__ == "__main__":
62
66
  cli(obj={})
primitive/client.py CHANGED
@@ -8,6 +8,8 @@ from .simulations.actions import Simulations
8
8
  from .hardware.actions import Hardware
9
9
  from .lint.actions import Lint
10
10
  from .agent.actions import Agent
11
+ from .git.actions import Git
12
+ from .daemons.actions import Daemons
11
13
 
12
14
  from loguru import logger
13
15
 
@@ -59,6 +61,8 @@ class Primitive:
59
61
  self.hardware: Hardware = Hardware(self)
60
62
  self.lint: Lint = Lint(self)
61
63
  self.agent: Agent = Agent(self)
64
+ self.git: Git = Git(self)
65
+ self.daemons: Daemons = Daemons(self)
62
66
 
63
67
  def get_host_config(self):
64
68
  self.full_config = read_config_file()
@@ -0,0 +1,75 @@
1
+ import platform
2
+ import typing
3
+
4
+ if typing.TYPE_CHECKING:
5
+ from ..client import Primitive
6
+
7
+ from .launch_agents import (
8
+ full_launch_agent_install,
9
+ full_launch_agent_uninstall,
10
+ start_launch_agent,
11
+ stop_launch_agent,
12
+ view_launch_agent_logs,
13
+ )
14
+
15
+ from .launch_service import (
16
+ full_service_install,
17
+ full_service_uninstall,
18
+ start_service,
19
+ stop_service,
20
+ view_service_logs,
21
+ )
22
+
23
+
24
+ class Daemons:
25
+ def __init__(self, primitive) -> None:
26
+ self.primitive: Primitive = primitive
27
+ self.os_family = platform.system()
28
+
29
+ def install(self):
30
+ result = True
31
+ if self.os_family == "Darwin":
32
+ full_launch_agent_install()
33
+ elif self.os_family == "Linux":
34
+ full_service_install()
35
+ elif self.os_family == "Windows":
36
+ print("Not Implemented")
37
+ return result
38
+
39
+ def uninstall(self):
40
+ result = True
41
+ if self.os_family == "Darwin":
42
+ full_launch_agent_uninstall()
43
+ elif self.os_family == "Linux":
44
+ full_service_uninstall()
45
+ elif self.os_family == "Windows":
46
+ print("Not Implemented")
47
+ return result
48
+
49
+ def stop(self) -> bool:
50
+ result = True
51
+ if self.os_family == "Darwin":
52
+ result = stop_launch_agent()
53
+ elif self.os_family == "Linux":
54
+ stop_service()
55
+ elif self.os_family == "Windows":
56
+ print("Not Implemented")
57
+ return result
58
+
59
+ def start(self) -> bool:
60
+ result = True
61
+ if self.os_family == "Darwin":
62
+ result = start_launch_agent()
63
+ elif self.os_family == "Linux":
64
+ start_service()
65
+ elif self.os_family == "Windows":
66
+ print("Not Implemented")
67
+ return result
68
+
69
+ def logs(self):
70
+ if self.os_family == "Darwin":
71
+ view_launch_agent_logs()
72
+ elif self.os_family == "Linux":
73
+ view_service_logs()
74
+ elif self.os_family == "Windows":
75
+ print("Not Implemented")
@@ -0,0 +1,65 @@
1
+ import click
2
+
3
+ from ..utils.printer import print_result
4
+ import typing
5
+
6
+ if typing.TYPE_CHECKING:
7
+ from ..client import Primitive
8
+
9
+
10
+ @click.group()
11
+ @click.pass_context
12
+ def cli(context):
13
+ """Daemon"""
14
+ pass
15
+
16
+
17
+ @cli.command("install")
18
+ @click.pass_context
19
+ def install_daemon_command(context):
20
+ """Install the full primitive daemon"""
21
+ primitive: Primitive = context.obj.get("PRIMITIVE")
22
+ result = primitive.daemons.install()
23
+ print_result(message=result, context=context)
24
+
25
+
26
+ @cli.command("uninstall")
27
+ @click.pass_context
28
+ def uninstall_daemon_command(context):
29
+ """Uninstall the full primitive Daemon"""
30
+ primitive: Primitive = context.obj.get("PRIMITIVE")
31
+ result = primitive.daemons.uninstall()
32
+ print_result(message=result, context=context)
33
+
34
+
35
+ @cli.command("stop")
36
+ @click.pass_context
37
+ def stop_daemon_command(context):
38
+ """Stop primitive Daemon"""
39
+ primitive: Primitive = context.obj.get("PRIMITIVE")
40
+ result = primitive.daemons.stop()
41
+ message = "stopping primitive daemon"
42
+ if context.obj["JSON"]:
43
+ message = result
44
+ print_result(message=message, context=context)
45
+
46
+
47
+ @cli.command("start")
48
+ @click.pass_context
49
+ def start_daemon_command(context):
50
+ """Start primitive Daemon"""
51
+ primitive: Primitive = context.obj.get("PRIMITIVE")
52
+ result = primitive.daemons.start()
53
+ message = "starting primitive daemon"
54
+ if context.obj["JSON"]:
55
+ message = result
56
+ print_result(message=message, context=context)
57
+
58
+
59
+ @cli.command("logs")
60
+ @click.pass_context
61
+ def log_daemon_command(context):
62
+ """Logs from primitive Daemon"""
63
+ primitive: Primitive = context.obj.get("PRIMITIVE")
64
+ result = primitive.daemons.logs()
65
+ print_result(message=result, context=context)
@@ -0,0 +1,144 @@
1
+ import os
2
+ from pathlib import Path
3
+ import subprocess
4
+
5
+ HOME_DIRECTORY = Path.home()
6
+ CURRENT_USER = str(HOME_DIRECTORY.expanduser()).lstrip("/Users/")
7
+
8
+ PRIMITIVE_BINARY_PATH = Path(HOME_DIRECTORY / ".pyenv" / "shims" / "primitive")
9
+ PRIMITIVE_LAUNCH_AGENT_FILEPATH = Path(
10
+ HOME_DIRECTORY / "Library" / "LaunchAgents" / "tech.primitive.agent.plist"
11
+ )
12
+ PRIMITIVE_LAUNCH_AGENT_LOGS = Path(
13
+ HOME_DIRECTORY / "Library" / "Logs" / "tech.primitive.agent.log"
14
+ )
15
+ PRIMITIVE_LAUNCH_AGENT_LABEL = "tech.primitive.agent"
16
+ PRIMITIVE_LAUNCH_AGENT_WORKING_DIR = Path(
17
+ HOME_DIRECTORY / "Logs" / "tech.primitive.agent.log"
18
+ )
19
+
20
+
21
+ def stop_launch_agent():
22
+ try:
23
+ stop_existing_process = f"launchctl stop {PRIMITIVE_LAUNCH_AGENT_LABEL}"
24
+ subprocess.check_output(stop_existing_process.split(" "))
25
+ return True
26
+ except subprocess.CalledProcessError as exception:
27
+ print("stop_launch_agent: ", exception)
28
+ return False
29
+
30
+
31
+ def start_launch_agent():
32
+ try:
33
+ start_new_agent = f"launchctl start {PRIMITIVE_LAUNCH_AGENT_LABEL}"
34
+ subprocess.check_output(start_new_agent.split(" "))
35
+ return True
36
+ except subprocess.CalledProcessError as exception:
37
+ print("start_launch_agent: ", exception)
38
+ return False
39
+
40
+
41
+ def unload_launch_agent():
42
+ try:
43
+ remove_existing_agent = f"launchctl unload -w {PRIMITIVE_LAUNCH_AGENT_FILEPATH}"
44
+ subprocess.check_output(remove_existing_agent.split(" "))
45
+ return True
46
+ except subprocess.CalledProcessError as exception:
47
+ print("remove_launch_agent: ", exception)
48
+ return False
49
+
50
+
51
+ def load_launch_agent():
52
+ try:
53
+ load_new_plist = f"launchctl load -w {PRIMITIVE_LAUNCH_AGENT_FILEPATH}"
54
+ subprocess.check_output(load_new_plist.split(" "))
55
+ return True
56
+ except subprocess.CalledProcessError as exception:
57
+ print("load_launch_agent: ", exception)
58
+ return False
59
+
60
+
61
+ def create_stdout_file():
62
+ if not PRIMITIVE_LAUNCH_AGENT_LOGS.exists():
63
+ PRIMITIVE_LAUNCH_AGENT_LOGS.parent.mkdir(parents=True, exist_ok=True)
64
+ PRIMITIVE_LAUNCH_AGENT_LOGS.touch()
65
+
66
+
67
+ def delete_stdout_file():
68
+ if PRIMITIVE_LAUNCH_AGENT_LOGS.exists():
69
+ PRIMITIVE_LAUNCH_AGENT_LOGS.unlink()
70
+
71
+
72
+ def populate_fresh_launch_agent():
73
+ PRIMITIVE_LAUNCH_AGENT_LOGS.parent.mkdir(parents=True, exist_ok=True)
74
+ PRIMITIVE_LAUNCH_AGENT_LOGS.touch()
75
+
76
+ if PRIMITIVE_LAUNCH_AGENT_FILEPATH.exists():
77
+ PRIMITIVE_LAUNCH_AGENT_FILEPATH.unlink()
78
+ PRIMITIVE_LAUNCH_AGENT_FILEPATH.parent.mkdir(parents=True, exist_ok=True)
79
+ PRIMITIVE_LAUNCH_AGENT_FILEPATH.touch()
80
+
81
+ PRIMITIVE_LAUNCH_AGENT_FILEPATH.write_text(
82
+ f"""<?xml version="1.0" encoding="UTF-8"?>
83
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
84
+ <plist version="1.0">
85
+ <dict>
86
+ <key>KeepAlive</key>
87
+ <true/>
88
+ <key>Label</key>
89
+ <string>{PRIMITIVE_LAUNCH_AGENT_LABEL}</string>
90
+ <key>LimitLoadToSessionType</key>
91
+ <array>
92
+ <string>Aqua</string>
93
+ <string>Background</string>
94
+ <string>LoginWindow</string>
95
+ <string>StandardIO</string>
96
+ </array>
97
+ <key>ProgramArguments</key>
98
+ <array>
99
+ <string>{PRIMITIVE_BINARY_PATH}</string>
100
+ <string>agent</string>
101
+ </array>
102
+ <key>RunAtLoad</key>
103
+ <true/>
104
+ <key>StandardErrorPath</key>
105
+ <string>{PRIMITIVE_LAUNCH_AGENT_LOGS}</string>
106
+ <key>StandardOutPath</key>
107
+ <string>{PRIMITIVE_LAUNCH_AGENT_LOGS}</string>
108
+ </dict>
109
+ </plist>""" # noqa: E501
110
+ )
111
+ PRIMITIVE_LAUNCH_AGENT_FILEPATH.chmod(0o644)
112
+ verify_launch_agent()
113
+
114
+
115
+ def verify_launch_agent():
116
+ plutil_check = f"plutil -lint {PRIMITIVE_LAUNCH_AGENT_FILEPATH}"
117
+ try:
118
+ subprocess.check_output(plutil_check.split(" "))
119
+ return True
120
+ except subprocess.CalledProcessError as exception:
121
+ print("verify_launch_agent: ", exception)
122
+ return False
123
+
124
+
125
+ def view_launch_agent_logs():
126
+ follow_logs = f"tail -f -n +1 {PRIMITIVE_LAUNCH_AGENT_LOGS}"
127
+ os.system(follow_logs)
128
+
129
+
130
+ def full_launch_agent_install():
131
+ stop_launch_agent()
132
+ unload_launch_agent()
133
+ populate_fresh_launch_agent()
134
+ create_stdout_file()
135
+ load_launch_agent()
136
+ start_launch_agent()
137
+
138
+
139
+ def full_launch_agent_uninstall():
140
+ stop_launch_agent()
141
+ unload_launch_agent()
142
+ if PRIMITIVE_LAUNCH_AGENT_FILEPATH.exists():
143
+ PRIMITIVE_LAUNCH_AGENT_FILEPATH.unlink()
144
+ delete_stdout_file()
@@ -0,0 +1,170 @@
1
+ import os
2
+ import configparser
3
+ import subprocess
4
+ from pathlib import Path
5
+
6
+ HOME_DIRECTORY = Path.home()
7
+
8
+ PRIMITIVE_BINARY_PATH = Path(HOME_DIRECTORY / ".pyenv" / "shims" / "primitive")
9
+
10
+ PRIMITIVE_AGENT_SERVICE = "tech.primitive.agent.service"
11
+ PRIMITIVE_AGENT_LOGS = "tech.primitive.agent.log"
12
+ PRIMITIVE_AGENT_SERVICE_FILEPATH = Path(
13
+ HOME_DIRECTORY / ".config" / "systemd" / "user" / PRIMITIVE_AGENT_SERVICE
14
+ )
15
+ PRIMITIVE_AGENT_LOGS_FILEPATH = Path(
16
+ HOME_DIRECTORY / ".cache" / "primitive" / PRIMITIVE_AGENT_LOGS
17
+ )
18
+
19
+
20
+ def stop_service():
21
+ try:
22
+ if is_service_active():
23
+ stop_existing_service = f"systemctl --user stop {PRIMITIVE_AGENT_SERVICE}"
24
+ subprocess.check_output(stop_existing_service.split(" "))
25
+ return True
26
+ except subprocess.CalledProcessError as exception:
27
+ print("stop_service: ", exception)
28
+ return False
29
+
30
+
31
+ def is_service_active():
32
+ try:
33
+ is_service_active = (
34
+ f"systemctl --user show {PRIMITIVE_AGENT_SERVICE} -p ActiveState --value"
35
+ )
36
+ output = subprocess.check_output(is_service_active.split(" ")).decode().strip()
37
+ return output == "active"
38
+ except subprocess.CalledProcessError as exception:
39
+ print("is_service_active: ", exception)
40
+ return False
41
+
42
+
43
+ def disable_service():
44
+ try:
45
+ if is_service_enabled():
46
+ disable_existing_service = (
47
+ f"systemctl --user disable {PRIMITIVE_AGENT_SERVICE}"
48
+ )
49
+ subprocess.check_output(disable_existing_service.split(" "))
50
+ return True
51
+ except subprocess.CalledProcessError as exception:
52
+ print("disable_service: ", exception)
53
+ return False
54
+
55
+
56
+ def is_service_enabled():
57
+ try:
58
+ is_service_active = (
59
+ f"systemctl --user show {PRIMITIVE_AGENT_SERVICE} -p UnitFileState --value" # noqa
60
+ )
61
+ output = subprocess.check_output(is_service_active.split(" ")).decode().strip()
62
+ return output == "enabled"
63
+ except subprocess.CalledProcessError as exception:
64
+ print("is_service_enabled: ", exception)
65
+ return False
66
+
67
+
68
+ def populate_service_file():
69
+ PRIMITIVE_AGENT_LOGS_FILEPATH.parent.mkdir(parents=True, exist_ok=True)
70
+ PRIMITIVE_AGENT_LOGS_FILEPATH.touch()
71
+
72
+ if PRIMITIVE_AGENT_SERVICE_FILEPATH.exists():
73
+ PRIMITIVE_AGENT_SERVICE_FILEPATH.unlink()
74
+ PRIMITIVE_AGENT_SERVICE_FILEPATH.parent.mkdir(parents=True, exist_ok=True)
75
+ PRIMITIVE_AGENT_SERVICE_FILEPATH.touch()
76
+
77
+ config = configparser.ConfigParser()
78
+ config.optionxform = str # type: ignore
79
+
80
+ config["Unit"] = {
81
+ "Description": "Primitive Agent",
82
+ "After": "network.target",
83
+ }
84
+
85
+ config["Service"] = {
86
+ "ExecStart": f"{PRIMITIVE_BINARY_PATH} agent",
87
+ "Restart": "always",
88
+ "StandardError": f"append:{PRIMITIVE_AGENT_LOGS_FILEPATH}",
89
+ "StandardOutput": f"append:{PRIMITIVE_AGENT_LOGS_FILEPATH}",
90
+ }
91
+
92
+ config["Install"] = {
93
+ "WantedBy": "default.target",
94
+ }
95
+
96
+ try:
97
+ with open(PRIMITIVE_AGENT_SERVICE_FILEPATH, "w") as service_file:
98
+ config.write(service_file)
99
+ except IOError as exception:
100
+ print(f"populate_service_file: {exception}")
101
+
102
+ PRIMITIVE_AGENT_SERVICE_FILEPATH.chmod(0o644)
103
+ verify_service_file()
104
+
105
+
106
+ def verify_service_file():
107
+ systemctl_check = (
108
+ f"systemctl --user show {PRIMITIVE_AGENT_SERVICE} -p CanStart --value"
109
+ )
110
+ try:
111
+ output = subprocess.check_output(systemctl_check.split(" ")).decode().strip()
112
+ if output == "no":
113
+ raise Exception(f"{systemctl_check} yielded {output}")
114
+ return True
115
+ except subprocess.CalledProcessError as exception:
116
+ print("verify_service_file: ", exception)
117
+ return False
118
+
119
+
120
+ def create_stdout_file():
121
+ if not PRIMITIVE_AGENT_LOGS_FILEPATH.exists():
122
+ PRIMITIVE_AGENT_LOGS_FILEPATH.parent.mkdir(parents=True, exist_ok=True)
123
+ PRIMITIVE_AGENT_LOGS_FILEPATH.touch()
124
+
125
+
126
+ def delete_stdout_file():
127
+ if PRIMITIVE_AGENT_LOGS_FILEPATH.exists():
128
+ PRIMITIVE_AGENT_LOGS_FILEPATH.unlink()
129
+
130
+
131
+ def enable_service():
132
+ try:
133
+ enable_service = f"systemctl --user enable {PRIMITIVE_AGENT_SERVICE}"
134
+ subprocess.check_output(enable_service.split(" "))
135
+ return True
136
+ except subprocess.CalledProcessError as exception:
137
+ print("enable_service: ", exception)
138
+ return False
139
+
140
+
141
+ def start_service():
142
+ try:
143
+ start_new_service = f"systemctl --user start {PRIMITIVE_AGENT_SERVICE}"
144
+ subprocess.check_output(start_new_service.split(" "))
145
+ return True
146
+ except subprocess.CalledProcessError as exception:
147
+ print("start_service: ", exception)
148
+ return False
149
+
150
+
151
+ def view_service_logs():
152
+ follow_logs = f"tail -f -n +1 {PRIMITIVE_AGENT_LOGS_FILEPATH}"
153
+ os.system(follow_logs)
154
+
155
+
156
+ def full_service_install():
157
+ stop_service()
158
+ disable_service()
159
+ populate_service_file()
160
+ create_stdout_file()
161
+ enable_service()
162
+ start_service()
163
+
164
+
165
+ def full_service_uninstall():
166
+ stop_service()
167
+ disable_service()
168
+ if PRIMITIVE_AGENT_SERVICE_FILEPATH.exists():
169
+ PRIMITIVE_AGENT_SERVICE_FILEPATH.unlink()
170
+ delete_stdout_file()
File without changes
@@ -0,0 +1,42 @@
1
+ from pathlib import Path
2
+ from primitive.utils.actions import BaseAction
3
+ from loguru import logger
4
+ import os
5
+
6
+
7
+ class Git(BaseAction):
8
+ def get_github_access_token(self) -> str:
9
+ query = """
10
+ query githubAppToken{
11
+ githubAppToken {
12
+ token
13
+ }
14
+ }
15
+ """
16
+
17
+ filters = {}
18
+ variables = {
19
+ "filters": filters,
20
+ }
21
+ result = self.primitive.session.execute(query, variable_values=variables)
22
+ return result
23
+
24
+ def download_git_repository_at_ref(
25
+ self,
26
+ git_repo_full_name: str,
27
+ github_access_token: str,
28
+ git_ref: str = "main",
29
+ destination: Path = Path.cwd(),
30
+ ) -> Path:
31
+ logger.debug(f"Downloading source code from {git_repo_full_name} {git_ref}")
32
+ # TODO: switch to subprocess.run or subprocess.Popen
33
+ url = f"https://api.github.com/repos/{git_repo_full_name}/tarball/{git_ref}"
34
+ untar_dir = Path(destination).joinpath(git_repo_full_name.split("/")[-1])
35
+ untar_dir.mkdir(parents=True, exist_ok=True)
36
+
37
+ result = os.system(
38
+ f"curl -s -L -H 'Accept: application/vnd.github+json' -H 'Authorization: Bearer {github_access_token}' -H 'X-GitHub-Api-Version: 2022-11-28' {url} | tar zx --strip-components 1 -C {untar_dir}"
39
+ )
40
+ if result != 0:
41
+ raise Exception("Failed to download repository")
42
+ return untar_dir
@@ -0,0 +1,49 @@
1
+ import click
2
+ from pathlib import Path
3
+ import typing
4
+ from ..utils.printer import print_result
5
+ import os
6
+
7
+ if typing.TYPE_CHECKING:
8
+ from ..client import Primitive
9
+
10
+
11
+ import typing
12
+
13
+ if typing.TYPE_CHECKING:
14
+ from ..client import Primitive
15
+
16
+
17
+ @click.group()
18
+ @click.pass_context
19
+ def cli(context):
20
+ """Git related commands"""
21
+ pass
22
+
23
+
24
+ @cli.command("download-ref")
25
+ @click.pass_context
26
+ @click.argument("git_repo_full_name", type=str)
27
+ @click.argument("git_ref", default="main", type=str)
28
+ @click.option(
29
+ "--github-access-token",
30
+ type=str,
31
+ default=lambda: os.environ.get("GITHUB_ACCESS_TOKEN", ""),
32
+ )
33
+ @click.argument("destination", type=click.Path(exists=True), default=".")
34
+ def download_ref_command(
35
+ context,
36
+ git_repo_full_name: str,
37
+ git_ref: str = "main",
38
+ github_access_token: str = "",
39
+ destination: Path = Path.cwd(),
40
+ ):
41
+ primitive: Primitive = context.obj.get("PRIMITIVE")
42
+ result, message = primitive.git.download_git_repository_at_ref(
43
+ git_repo_full_name=git_repo_full_name,
44
+ git_ref=git_ref,
45
+ github_access_token=github_access_token,
46
+ destination=destination,
47
+ )
48
+ fg = "red" if not result else "green"
49
+ print_result(message, context=context, fg=fg)
primitive/lint/actions.py CHANGED
@@ -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...")
12
+ logger.debug("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."
@@ -48,14 +48,11 @@ class Lint(BaseAction):
48
48
  logger.debug("Linting complete.")
49
49
 
50
50
  message = ""
51
- if self.primitive.DEBUG:
52
- if result.stderr:
53
- logger.error("\n" + result.stderr)
54
- if result.stdout:
55
- logger.info("\n" + result.stdout)
56
- message = "See above logs for linter output."
57
- else:
58
- message = result.stderr
51
+ if result.stderr:
52
+ logger.error("\n" + result.stderr)
53
+ if result.stdout:
54
+ logger.info("\n" + result.stdout)
55
+ message = "See above logs for linter output."
59
56
 
60
57
  if result.returncode != 0:
61
58
  if not self.primitive.DEBUG:
@@ -45,6 +45,11 @@ fragment JobRunFragment on JobRun {
45
45
  createdAt
46
46
  updatedAt
47
47
  }
48
+ gitCommit {
49
+ sha
50
+ branch
51
+ repoFullName
52
+ }
48
53
  }
49
54
 
50
55
  query jobRuns(
@@ -156,3 +161,15 @@ query jobRuns(
156
161
  variables = {"input": input}
157
162
  result = self.primitive.session.execute(mutation, variable_values=variables)
158
163
  return result
164
+
165
+ def github_access_token_for_job_run(self, job_run_id: str):
166
+ query = gql(
167
+ """
168
+ query ghAppTokenForJobRun($jobRunId: GlobalID!) {
169
+ ghAppTokenForJobRun(jobRunId: $jobRunId)
170
+ }
171
+ """
172
+ )
173
+ variables = {"jobRunId": job_run_id}
174
+ result = self.primitive.session.execute(query, variable_values=variables)
175
+ return result["ghAppTokenForJobRun"]
primitive/utils/files.py CHANGED
@@ -1,15 +1,22 @@
1
1
  from typing import List, Tuple
2
2
  from pathlib import Path
3
3
  from loguru import logger
4
+ import os
4
5
 
5
6
 
6
7
  def find_files_for_extension(source: Path, extensions: Tuple[str]) -> List[Path]:
7
8
  matching_files = []
8
9
  logger.debug(f"Looking for files at {source} with extensions {extensions}")
9
- for dirpath, dirnames, filenames in source.walk():
10
+ # for those on python 3.12+
11
+ # for dirpath, dirnames, filenames in source.walk():
12
+ # for filename in filenames:
13
+ # if filename.endswith(extensions):
14
+ # matching_files.append(dirpath.joinpath(filename))
15
+
16
+ for dirpath, dirnames, filenames in os.walk(source):
10
17
  for filename in filenames:
11
18
  if filename.endswith(extensions):
12
- matching_files.append(dirpath.joinpath(filename))
19
+ matching_files.append(Path(dirpath).joinpath(filename))
13
20
  logger.debug(
14
21
  f"Found {len(matching_files)} following files that match: {matching_files}"
15
22
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: primitive
3
- Version: 0.1.11
3
+ Version: 0.1.13
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,34 +1,41 @@
1
- primitive/__about__.py,sha256=cEU7i5k8S0WhpV8qykCw-GDDdmS5X4Rl6vZ5cCkC7EE,129
1
+ primitive/__about__.py,sha256=xMBcV0dgZBAcVvP4QqmQUK6YrJ-xPRUcJbx7NA4hQWw,129
2
2
  primitive/__init__.py,sha256=bwKdgggKNVssJFVPfKSxqFMz4IxSr54WWbmiZqTMPNI,106
3
- primitive/cli.py,sha256=LXSpPtqQLxkOXsnsbs4UN3n7G_pHCz8AkBFCrk7Is4g,1796
4
- primitive/client.py,sha256=3RMRNY4ZiGT2OKnZtquWyrXjRSgDe5HlZvtFNKZErLQ,2083
5
- primitive/agent/actions.py,sha256=f6bv2va1YrDfrzOVK2kTnelRQ2oEvmrLAmsPrxMCh7E,2893
3
+ primitive/cli.py,sha256=2IEAdHj93nmWaDauyua3hw0kFsqhW2zPPVB7W05d7-Q,1978
4
+ primitive/client.py,sha256=A2Ir9KRnm7VptG6iUba4zihx12SzQZLnsF19neyJmdY,2229
5
+ primitive/agent/actions.py,sha256=8eQDvUy0THT-zVvSTVgrDAsthRdSIpmhCkDr0iTXqDE,3673
6
6
  primitive/agent/commands.py,sha256=-dVDilELfkGfbZB7qfEPs77Dm1oT62qJj4tsIk4KoxI,254
7
7
  primitive/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  primitive/auth/actions.py,sha256=N2bGcwXNsB89pzs66gF9A5_WzUScY5fhfOyWixqo2y8,1054
9
9
  primitive/auth/commands.py,sha256=JahUq0E2e7Xa-FX1WEUv7TgM6ieDvNH4VwRRtxAW7HE,2340
10
+ primitive/daemons/actions.py,sha256=Nt3yNtbBhen0jK4sRsH_N7AP3UBuyL48VaUhtC7wYq8,2015
11
+ primitive/daemons/commands.py,sha256=-Muh-6ib4uAVtPn_67AcMrDwuCwYlCnRQozCi2Xurmk,1726
12
+ primitive/daemons/launch_agents.py,sha256=xR_JDL93mt5-fSbj-v3qvYFvLqfn486fPNbNxZOb1wU,4412
13
+ primitive/daemons/launch_service.py,sha256=3ktr0n0rSNA-IV6hQ8hbG50L2Y3V8-IeW79C8CrZi8U,5203
10
14
  primitive/files/actions.py,sha256=f4JN3QFB2WXw-0JpnE-4-movnqtvXIpCrGd_9pdkeW4,2624
11
15
  primitive/files/commands.py,sha256=DDizo3xJnU3KLUBTMeeM72viVpnJinLwxs84tmqKhqo,810
16
+ primitive/git/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ primitive/git/actions.py,sha256=fepcl5529w_hsaC6fBw9f-QHeyqNjGXz8HI5ebzbZMs,1386
18
+ primitive/git/commands.py,sha256=64B2STTOn0dwVDmJHqEwekmIqKMfSyBBFwKg29Wt8Aw,1230
12
19
  primitive/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
20
  primitive/graphql/sdk.py,sha256=BhCGmDtc4sNnH8CxbQSJyFwOZ-ZSqMtjsxMB3JRBhPw,1456
14
21
  primitive/hardware/actions.py,sha256=Ea3_2E3F_3WapV60g_mOIcpXhadoknwihR7slXyUWtk,18840
15
22
  primitive/hardware/commands.py,sha256=QE7LLeFdfOqlvz3JwdwJJRZAY3fHI1zB9kYmmDajpq0,1477
16
- primitive/lint/actions.py,sha256=FVigksFrADPDD3zHS0g0riyVrge7eEtwWk5PG2aZv9A,2352
23
+ primitive/lint/actions.py,sha256=PDw0fkIkI5hHHjoHaAiXvQUocwHZkgB2mfI-LGMl6TI,2267
17
24
  primitive/lint/commands.py,sha256=3CZvkOEMpJspJWmaQzA5bpPKx0_VCijQIXA9l-eTnZE,487
18
25
  primitive/projects/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
19
- primitive/projects/actions.py,sha256=h1rgt3tWUPye0gnFvIZzZz7ibTbxyBS4q9RryjkVLzY,3600
26
+ primitive/projects/actions.py,sha256=4Fwq2KTBuhDy8SfEIqPhR_8rW2rAL76cGWIQS40a96A,4039
20
27
  primitive/simulations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
28
  primitive/simulations/actions.py,sha256=YR0oxxd7_kuUIH77BWZLUs9rLRiSJztPPhpgDJU2PbY,1267
22
29
  primitive/utils/actions.py,sha256=HOFrmM3-0A_A3NS84MqrZ6JmQEiiPSoDqEeuu6b_qfQ,196
23
30
  primitive/utils/config.py,sha256=DlFM5Nglo22WPtbpZSVtH7NX-PTMaKYlcrUE7GPRG4c,1058
24
- primitive/utils/files.py,sha256=Ug5UqrS9eZoTd08EFtgyyp8flsJTfsG9CwXBWmI-yFA,606
31
+ primitive/utils/files.py,sha256=Yv__bQes3YIlzhOT9kVxtYhoA5CmUjPSvphl9PZ41k4,867
25
32
  primitive/utils/git.py,sha256=1qNOu8X-33CavmrD580BmrFhD_WVO9PGWHUUboXJR_g,663
26
33
  primitive/utils/memory_size.py,sha256=4xfha21kW82nFvOTtDFx9Jk2ZQoEhkfXii-PGNTpIUk,3058
27
34
  primitive/utils/printer.py,sha256=f1XUpqi5dkTL3GWvYRUGlSwtj2IxU1q745T4Fxo7Tn4,370
28
35
  primitive/utils/shell.py,sha256=-7UjQaBqSGHzEEyX8pNjeYFFP0P3lVnDV0OkgPz1qHU,1050
29
36
  primitive/utils/verible.py,sha256=QYczN1IvxODfj4jeq0nqjFuF0Oi0Zdx-Q32ySOJgcw8,2205
30
- primitive-0.1.11.dist-info/METADATA,sha256=-KZVIgcT4_othtd9SQqixiI7PhsJNcgUzeO7wQxRw7s,1818
31
- primitive-0.1.11.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
32
- primitive-0.1.11.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
33
- primitive-0.1.11.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
34
- primitive-0.1.11.dist-info/RECORD,,
37
+ primitive-0.1.13.dist-info/METADATA,sha256=fUowLloXzAcRrLMhUloZAmRpiOLiVMXH8PZspjLb0YE,1818
38
+ primitive-0.1.13.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
39
+ primitive-0.1.13.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
40
+ primitive-0.1.13.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
41
+ primitive-0.1.13.dist-info/RECORD,,