primitive 0.1.53__py3-none-any.whl → 0.1.54__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.53"
4
+ __version__ = "0.1.54"
@@ -120,7 +120,7 @@ class Agent(BaseAction):
120
120
  )
121
121
 
122
122
  runner = AgentRunner(
123
- self.primitive,
123
+ primitive=self.primitive,
124
124
  source_dir=source_dir,
125
125
  job_id=job_run["id"],
126
126
  job_slug=job_run["job"]["slug"],
@@ -1,6 +1,6 @@
1
1
  from subprocess import Popen, PIPE
2
- import threading
3
2
  import shlex
3
+ import selectors
4
4
  from loguru import logger
5
5
 
6
6
 
@@ -21,8 +21,7 @@ class Process:
21
21
 
22
22
  def start(self):
23
23
  # Start the process
24
- logger.info(f"cmd: {self.cmd}")
25
- logger.info(f"workdir: {self.workdir}")
24
+ self.sel = selectors.DefaultSelector()
26
25
  self.process = Popen(
27
26
  self.cmd,
28
27
  env=self.env,
@@ -32,40 +31,33 @@ class Process:
32
31
  text=True,
33
32
  )
34
33
 
35
- # Function to read and log output from a pipe
36
- def log_output(pipe, level):
37
- for line in iter(pipe.readline, ""):
38
- if line:
39
- logger.log(level, line.rstrip())
40
- if level == "ERROR":
41
- self._errors += 1
34
+ self.sel.register(self.process.stdout, selectors.EVENT_READ)
35
+ self.sel.register(self.process.stderr, selectors.EVENT_READ)
42
36
 
43
- pipe.close()
37
+ def probe_logs(self):
38
+ for key, _ in self.sel.select():
39
+ data = key.fileobj.readline()
40
+ if not data:
41
+ continue
44
42
 
45
- # Create threads for stdout and stderr
46
- self.stdout_thread = threading.Thread(
47
- target=log_output, args=(self.process.stdout, "INFO")
48
- )
49
- self.stderr_thread = threading.Thread(
50
- target=log_output, args=(self.process.stderr, "ERROR")
51
- )
52
-
53
- # Start the threads
54
- self.stdout_thread.start()
55
- self.stderr_thread.start()
43
+ if key.fileobj is self.process.stdout:
44
+ logger.info(data.rstrip())
45
+ elif key.fileobj is self.process.stderr:
46
+ logger.error(data.rstrip())
47
+ self._errors += 1
56
48
 
57
49
  def wait(self):
58
- if self.process:
59
- # Wait for the process to complete
60
- self.process.wait()
61
- # Wait for the threads to finish reading output
62
- self.stdout_thread.join()
63
- self.stderr_thread.join()
50
+ while True:
51
+ self.probe_logs()
52
+ if not self.is_running():
53
+ break
54
+
55
+ return self.finish()
64
56
 
65
57
  def run(self):
66
58
  """Start and wait for the process."""
67
59
  self.start()
68
- self.wait()
60
+ return self.wait()
69
61
 
70
62
  def is_running(self):
71
63
  """Check if the process is still running."""
@@ -73,10 +65,13 @@ class Process:
73
65
 
74
66
  def finish(self):
75
67
  """Make sure that logging finishes"""
76
- self.stderr_thread.join()
77
- self.stderr_thread.join()
68
+ if self.process:
69
+ self.sel.unregister(self.process.stdout)
70
+ self.sel.unregister(self.process.stderr)
71
+ self.process.stdout.close()
72
+ self.process.stderr.close()
78
73
 
79
- return self.process.poll()
74
+ return self.process.poll()
80
75
 
81
76
  def terminate(self):
82
77
  """Terminate the process."""
primitive/agent/runner.py CHANGED
@@ -2,6 +2,7 @@ import yaml
2
2
  import sys
3
3
  import typing
4
4
  import os
5
+ import threading
5
6
  from time import sleep
6
7
  from typing import TypedDict, Iterable, List, Optional, Dict
7
8
  from pathlib import Path, PurePath
@@ -111,10 +112,8 @@ class AgentRunner:
111
112
  logger.info(f"Beginning step {step['name']}")
112
113
 
113
114
  # Define step proc
114
- logger.info(f"first cmd: {step['cmd']}")
115
- logger.info(f"first workdir: {Path(self.source_dir / step['workdir'])}")
116
115
  proc = Process(
117
- step["cmd"],
116
+ cmd=step["cmd"],
118
117
  workdir=Path(self.source_dir / step["workdir"]),
119
118
  env=environment,
120
119
  )
@@ -127,24 +126,31 @@ class AgentRunner:
127
126
  conclusion = "failure"
128
127
  break
129
128
 
130
- # Check for updates to status while running
131
- while proc.is_running():
132
- status = self.primitive.jobs.get_job_status(self.job_id)
129
+ def status_check():
130
+ while proc.is_running():
131
+ # Check job status
132
+ status = self.primitive.jobs.get_job_status(self.job_id)
133
+ status_value = status["jobRun"]["status"]
133
134
 
134
- status_value = status["jobRun"]["status"]
135
+ # TODO: Should probably use request_cancelled or something
136
+ # once we change it, we'll have to call conclude w/ cancelled status
137
+ if status_value == "completed":
138
+ logger.warning("Job cancelled by user")
139
+ proc.terminate()
140
+ return
135
141
 
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
142
+ sleep(5)
142
143
 
143
- sleep(5)
144
+ status_thread = threading.Thread(target=status_check)
145
+ status_thread.start()
144
146
 
145
- returncode = proc.finish()
147
+ # Wait for proc to finish
148
+ returncode = proc.wait()
146
149
  total_errors += proc.errors
147
150
 
151
+ # Wait for status check
152
+ status_thread.join()
153
+
148
154
  # Collect artifacts
149
155
  if "artifacts" in step:
150
156
  self.collect_artifacts(step)
@@ -1,5 +1,6 @@
1
1
  import typing
2
2
  import shutil
3
+ from loguru import logger
3
4
  from pathlib import Path, PurePath
4
5
  from ..utils.cache import get_artifacts_cache
5
6
 
@@ -22,25 +23,39 @@ class Uploader:
22
23
  # Scan artifacts directory
23
24
  artifacts_dir = get_artifacts_cache()
24
25
 
25
- subdirs = [
26
- job_cache for job_cache in artifacts_dir.iterdir() if job_cache.is_dir()
27
- ]
26
+ subdirs = sorted(
27
+ [job_cache for job_cache in artifacts_dir.iterdir() if job_cache.is_dir()],
28
+ key=lambda p: p.stat().st_ctime,
29
+ )
28
30
 
29
31
  for job_cache in subdirs:
30
32
  job_run_id = job_cache.name
31
- files = [file for file in job_cache.rglob("*") if file.is_file()]
33
+
34
+ files = sorted(
35
+ [
36
+ w_path / file
37
+ for w_path, _, w_files in job_cache.walk()
38
+ for file in w_files
39
+ ],
40
+ key=lambda p: p.stat().st_size,
41
+ )
32
42
 
33
43
  file_ids = []
34
44
  for file in files:
35
- file_ids.append(
36
- self.upload_file(
37
- file,
38
- prefix=str(PurePath(file).relative_to(job_cache.parent).parent),
39
- )
45
+ upload_id = self.upload_file(
46
+ file,
47
+ prefix=str(PurePath(file).relative_to(job_cache.parent).parent),
40
48
  )
41
49
 
50
+ if upload_id:
51
+ file_ids.append(upload_id)
52
+ continue
53
+
54
+ logger.error(f"Unable to upload file {file}")
55
+
42
56
  # Update job run
43
- self.primitive.jobs.job_run_update(id=job_run_id, file_ids=file_ids)
57
+ if len(file_ids) > 0:
58
+ self.primitive.jobs.job_run_update(id=job_run_id, file_ids=file_ids)
44
59
 
45
60
  # Clean up job cache
46
61
  shutil.rmtree(path=job_cache)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: primitive
3
- Version: 0.1.53
3
+ Version: 0.1.54
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,13 +1,13 @@
1
- primitive/__about__.py,sha256=eX7mKUzZD5kknoBHuD860zr_YILfDsBdXo5NYHeClXI,130
1
+ primitive/__about__.py,sha256=xmju9tTiBP_3gkTXO3-jul0M4FsAwKumYeIq-7FHnI4,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=m1FGvtOHdrZRP-YC31bFoUj-ZunXlFFW4NnPH9Bpzc0,5499
6
6
  primitive/agent/commands.py,sha256=-dVDilELfkGfbZB7qfEPs77Dm1oT62qJj4tsIk4KoxI,254
7
- primitive/agent/process.py,sha256=rbRB8MOxhTtMax-182Cwy_KP2I1fPyXLiu9p_GIE15Y,2451
7
+ primitive/agent/process.py,sha256=2ZY3YoJHvoukrsCAZIt-AF2YKY4HEO5_jWji5K3W9fM,2267
8
8
  primitive/agent/provision.py,sha256=3EEzOV-ria6zf-pvfNddad1lzzd1QmfKInTIjmwX71Y,1673
9
- primitive/agent/runner.py,sha256=tYjzQyfd1rEAmkBQ5C8ghryR2weH9J0hTXSPS5ijapM,6490
10
- primitive/agent/uploader.py,sha256=ngbynmQzxKVNMp7VWkTWW7vZIN_rnnRzYC6OAL21n1k,1378
9
+ primitive/agent/runner.py,sha256=hDh6ip_Jb0EsvygrB7-GWVTOf-jN8E7H0RgCGOdQnPU,6634
10
+ primitive/agent/uploader.py,sha256=mq8WAcjORHDFNYArvMKNRpvrke9tCMh3ZSTOKp-oUpg,1798
11
11
  primitive/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  primitive/auth/actions.py,sha256=2vZEC3LLAXj6eBBmt_2OEDEcBIb3uLkkNjgbZTIaQsY,1095
13
13
  primitive/auth/commands.py,sha256=JahUq0E2e7Xa-FX1WEUv7TgM6ieDvNH4VwRRtxAW7HE,2340
@@ -46,8 +46,8 @@ primitive/utils/memory_size.py,sha256=4xfha21kW82nFvOTtDFx9Jk2ZQoEhkfXii-PGNTpIU
46
46
  primitive/utils/printer.py,sha256=f1XUpqi5dkTL3GWvYRUGlSwtj2IxU1q745T4Fxo7Tn4,370
47
47
  primitive/utils/shell.py,sha256=-7UjQaBqSGHzEEyX8pNjeYFFP0P3lVnDV0OkgPz1qHU,1050
48
48
  primitive/utils/verible.py,sha256=r7c_hfqvL0UicMmIzK3Cy_BfZI1ZpcfBeLqKEWFWqJo,2252
49
- primitive-0.1.53.dist-info/METADATA,sha256=4-y4DVEtRwaFuYTycrfi5QPHGQuzdJBzGrIzkRIskoo,3782
50
- primitive-0.1.53.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
51
- primitive-0.1.53.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
52
- primitive-0.1.53.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
53
- primitive-0.1.53.dist-info/RECORD,,
49
+ primitive-0.1.54.dist-info/METADATA,sha256=6_qZmgPk3T3V14hCWBIUWrKnK7fF7CrCqoVwvFVw7rA,3782
50
+ primitive-0.1.54.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
51
+ primitive-0.1.54.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
52
+ primitive-0.1.54.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
53
+ primitive-0.1.54.dist-info/RECORD,,