primitive 0.1.48__py3-none-any.whl → 0.1.49__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@primitive.tech>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "0.1.48"
4
+ __version__ = "0.1.49"
@@ -1,5 +1,4 @@
1
1
  import sys
2
- import shutil
3
2
  from time import sleep
4
3
  from primitive.utils.actions import BaseAction
5
4
  from loguru import logger
@@ -130,7 +129,7 @@ class Agent(BaseAction):
130
129
  runner.execute()
131
130
 
132
131
  # Clean up
133
- shutil.rmtree(path=downloaded_git_repository_dir)
132
+ # shutil.rmtree(path=downloaded_git_repository_dir)
134
133
 
135
134
  sleep(5)
136
135
  except KeyboardInterrupt:
@@ -8,9 +8,11 @@ class Process:
8
8
  def __init__(
9
9
  self,
10
10
  cmd,
11
+ env,
11
12
  workdir: str = ".",
12
13
  ):
13
14
  self.cmd = shlex.split(cmd)
15
+ self.env = env
14
16
  self.workdir = workdir
15
17
  self.process = None
16
18
  self.stdout_thread = None
@@ -22,7 +24,12 @@ class Process:
22
24
  logger.info(f"cmd: {self.cmd}")
23
25
  logger.info(f"workdir: {self.workdir}")
24
26
  self.process = Popen(
25
- self.cmd, cwd=self.workdir, stdout=PIPE, stderr=PIPE, text=True
27
+ self.cmd,
28
+ env=self.env,
29
+ cwd=self.workdir,
30
+ stdout=PIPE,
31
+ stderr=PIPE,
32
+ text=True,
26
33
  )
27
34
 
28
35
  # Function to read and log output from a pipe
@@ -0,0 +1,50 @@
1
+ import sys
2
+ from subprocess import Popen, PIPE
3
+ from pathlib import Path
4
+ from typing import Dict
5
+
6
+
7
+ class ProvisionPython:
8
+ def __init__(self, source_dir: Path, requirements_path: Path):
9
+ self.source_dir = source_dir
10
+ self.requirements_path = requirements_path
11
+
12
+ def create_env(self) -> Dict:
13
+ cmd = f"{sys.executable} -m ensurepip"
14
+ proc = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True, text=True)
15
+ proc.wait()
16
+
17
+ cmd = f"{sys.executable} -m pip install virtualenv"
18
+ proc = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True, text=True)
19
+ proc.wait()
20
+
21
+ cmd = f"{sys.executable} -m virtualenv {self.source_dir / "venv"}"
22
+ proc = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True, text=True)
23
+ proc.wait()
24
+
25
+ cmd = f"source {self.source_dir / "venv" / "bin" / "activate"} && env"
26
+ proc = Popen(cmd, stdout=PIPE, stderr=PIPE, shell=True, text=True)
27
+ proc.wait()
28
+
29
+ # Read the output and decode it
30
+ output, _ = proc.communicate()
31
+
32
+ # Split the output into lines and parse it into a dictionary
33
+ env_vars = {}
34
+
35
+ for line in output.splitlines():
36
+ key, value = line.split("=", 1)
37
+ env_vars[key] = value
38
+
39
+ cmd = f"python -m pip install -r {self.requirements_path}"
40
+ proc = Popen(
41
+ cmd,
42
+ env=env_vars,
43
+ stdout=PIPE,
44
+ stderr=PIPE,
45
+ shell=True,
46
+ text=True,
47
+ )
48
+ proc.wait()
49
+
50
+ return env_vars
primitive/agent/runner.py CHANGED
@@ -1,11 +1,13 @@
1
1
  import yaml
2
2
  import sys
3
3
  import typing
4
+ import os
4
5
  from time import sleep
5
- from typing import TypedDict, Iterable, List
6
+ from typing import TypedDict, Iterable, List, Optional, Dict
6
7
  from pathlib import Path, PurePath
7
8
  from loguru import logger
8
9
  from .process import Process
10
+ from .provision import ProvisionPython
9
11
  from ..utils.cache import get_artifacts_cache
10
12
  from ..utils.files import find_files_for_extension
11
13
 
@@ -32,6 +34,7 @@ class JobStep(TypedDict):
32
34
 
33
35
  class JobDescription(TypedDict):
34
36
  name: str
37
+ provision: str
35
38
  steps: List[JobStep]
36
39
 
37
40
 
@@ -92,6 +95,16 @@ class AgentRunner:
92
95
  logger.info(f"Executing {self.job_slug} job")
93
96
  self.primitive.jobs.job_run_update(self.job_id, status="request_in_progress")
94
97
 
98
+ # Initial environment is the system env
99
+ environment = os.environ
100
+ if "provision" in self.job:
101
+ logger.info(f"Provisioning for {self.job['provision']} environment")
102
+ environment = self.provision()
103
+
104
+ if not environment:
105
+ self.conclude("failure")
106
+ return
107
+
95
108
  conclusion = None
96
109
  total_errors = 0
97
110
  for step in self.steps():
@@ -100,7 +113,11 @@ class AgentRunner:
100
113
  # Define step proc
101
114
  logger.info(f"first cmd: {step['cmd']}")
102
115
  logger.info(f"first workdir: {Path(self.source_dir / step['workdir'])}")
103
- proc = Process(step["cmd"], workdir=Path(self.source_dir / step["workdir"]))
116
+ proc = Process(
117
+ step["cmd"],
118
+ workdir=Path(self.source_dir / step["workdir"]),
119
+ env=environment,
120
+ )
104
121
 
105
122
  # Try to start
106
123
  try:
@@ -114,7 +131,14 @@ class AgentRunner:
114
131
  while proc.is_running():
115
132
  status = self.primitive.jobs.get_job_status(self.job_id)
116
133
 
117
- logger.info(f"Step status: {status}")
134
+ status_value = status["jobRun"]["status"]
135
+
136
+ # TODO: Should probably use request_cancelled or something
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
118
142
 
119
143
  sleep(5)
120
144
 
@@ -136,6 +160,9 @@ class AgentRunner:
136
160
  logger.error(f"Job failed with {total_errors} errors.")
137
161
  conclusion = "failure"
138
162
 
163
+ self.conclude(conclusion)
164
+
165
+ def conclude(self, conclusion: str) -> None:
139
166
  self.primitive.jobs.job_run_update(
140
167
  self.job_id, status="request_completed", conclusion=conclusion
141
168
  )
@@ -143,6 +170,20 @@ class AgentRunner:
143
170
  logger.info(f"Completed {self.job_slug} job")
144
171
  logger.remove(self.logger_handle)
145
172
 
173
+ def provision(self) -> Optional[Dict]:
174
+ match self.job["provision"]:
175
+ case "python":
176
+ requirements_glob = self.source_dir.rglob("requirements.txt")
177
+
178
+ requirements_path = next(requirements_glob, None)
179
+
180
+ if not requirements_path:
181
+ logger.error("Unable to locate requirements.txt")
182
+ return None
183
+
184
+ prov = ProvisionPython(self.source_dir, requirements_path)
185
+ return prov.create_env()
186
+
146
187
  def collect_artifacts(self, step: JobStep) -> None:
147
188
  # str(PurePath(file_path).relative_to(Path(source))
148
189
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: primitive
3
- Version: 0.1.48
3
+ Version: 0.1.49
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,11 +1,12 @@
1
- primitive/__about__.py,sha256=SyUvBWaydpIhAgoSQiQhzs0YgMe3ddiXcykw6bCIZ9o,130
1
+ primitive/__about__.py,sha256=aO6YNrTRSljxoBea_ti6ZOsiQThBvDI_TcvwjL4xAjQ,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=d_garPm3rl5uMEu-g681yMYEnBqfUDeEPeeKl_VEd64,5489
5
+ primitive/agent/actions.py,sha256=R5ZvXD75H5XWdTvpmL5PDoSYnj7MCcfaDNQC0cqW-FU,5477
6
6
  primitive/agent/commands.py,sha256=-dVDilELfkGfbZB7qfEPs77Dm1oT62qJj4tsIk4KoxI,254
7
- primitive/agent/process.py,sha256=p9YkB1Emf9x3JJFcGtqN70om8_I2r0jmn5bkrQq_ggE,2340
8
- primitive/agent/runner.py,sha256=rB9DlTDdVo7ks-vagGZbCC4SunJEUQKlMaTUpLk1fRY,5051
7
+ primitive/agent/process.py,sha256=rbRB8MOxhTtMax-182Cwy_KP2I1fPyXLiu9p_GIE15Y,2451
8
+ primitive/agent/provision.py,sha256=L0N0uxyhToK4yklYO4exdlJRUk3SpK5h4DJ9egB35w4,1531
9
+ primitive/agent/runner.py,sha256=tYjzQyfd1rEAmkBQ5C8ghryR2weH9J0hTXSPS5ijapM,6490
9
10
  primitive/agent/uploader.py,sha256=ngbynmQzxKVNMp7VWkTWW7vZIN_rnnRzYC6OAL21n1k,1378
10
11
  primitive/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
12
  primitive/auth/actions.py,sha256=2vZEC3LLAXj6eBBmt_2OEDEcBIb3uLkkNjgbZTIaQsY,1095
@@ -45,8 +46,8 @@ primitive/utils/memory_size.py,sha256=4xfha21kW82nFvOTtDFx9Jk2ZQoEhkfXii-PGNTpIU
45
46
  primitive/utils/printer.py,sha256=f1XUpqi5dkTL3GWvYRUGlSwtj2IxU1q745T4Fxo7Tn4,370
46
47
  primitive/utils/shell.py,sha256=-7UjQaBqSGHzEEyX8pNjeYFFP0P3lVnDV0OkgPz1qHU,1050
47
48
  primitive/utils/verible.py,sha256=r7c_hfqvL0UicMmIzK3Cy_BfZI1ZpcfBeLqKEWFWqJo,2252
48
- primitive-0.1.48.dist-info/METADATA,sha256=oJ3kcQ2ZOES15E4ErusuGrqgIbr_qNk8H56WAUw94MI,3782
49
- primitive-0.1.48.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
50
- primitive-0.1.48.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
51
- primitive-0.1.48.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
52
- primitive-0.1.48.dist-info/RECORD,,
49
+ primitive-0.1.49.dist-info/METADATA,sha256=Jc5CpG79Qfsp2IhMbRdrcm_Lg_RxUOLLOoqs2HaHJ8E,3782
50
+ primitive-0.1.49.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
51
+ primitive-0.1.49.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
52
+ primitive-0.1.49.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
53
+ primitive-0.1.49.dist-info/RECORD,,