primitive 0.2.30__py3-none-any.whl → 0.2.32__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 +1 -1
- primitive/agent/actions.py +0 -4
- primitive/agent/runner.py +47 -38
- primitive/daemons/launch_agents.py +1 -1
- primitive/jobs/actions.py +10 -0
- primitive/jobs/graphql/fragments.py +1 -0
- primitive/jobs/graphql/queries.py +7 -0
- primitive/utils/shell.py +3 -1
- {primitive-0.2.30.dist-info → primitive-0.2.32.dist-info}/METADATA +1 -1
- {primitive-0.2.30.dist-info → primitive-0.2.32.dist-info}/RECORD +13 -13
- {primitive-0.2.30.dist-info → primitive-0.2.32.dist-info}/WHEEL +0 -0
- {primitive-0.2.30.dist-info → primitive-0.2.32.dist-info}/entry_points.txt +0 -0
- {primitive-0.2.30.dist-info → primitive-0.2.32.dist-info}/licenses/LICENSE.txt +0 -0
primitive/__about__.py
CHANGED
primitive/agent/actions.py
CHANGED
@@ -100,10 +100,6 @@ class Agent(BaseAction):
|
|
100
100
|
# This should probably eventually be another daemon?
|
101
101
|
uploader.scan()
|
102
102
|
|
103
|
-
JobRun.objects.filter_by(
|
104
|
-
job_run_id=api_job_run.get("id"),
|
105
|
-
).delete()
|
106
|
-
|
107
103
|
sleep(5)
|
108
104
|
except KeyboardInterrupt:
|
109
105
|
logger.info("[agent] Stopping primitive agent...")
|
primitive/agent/runner.py
CHANGED
@@ -25,7 +25,11 @@ if typing.TYPE_CHECKING:
|
|
25
25
|
|
26
26
|
CHUNK_SIZE = 64 * 1024
|
27
27
|
ENV_VAR_LOOKUP_START = "_ENV_VAR_LOOKUP_START"
|
28
|
+
START_DELIMITER_SIZE = len(bytes(ENV_VAR_LOOKUP_START, encoding="utf-8"))
|
28
29
|
ENV_VAR_LOOKUP_END = "_ENV_VAR_LOOKUP_END"
|
30
|
+
END_DELIMITER_SIZE = len(bytes(ENV_VAR_LOOKUP_END, encoding="utf-8"))
|
31
|
+
|
32
|
+
assert CHUNK_SIZE > START_DELIMITER_SIZE + END_DELIMITER_SIZE
|
29
33
|
|
30
34
|
|
31
35
|
class Task(TypedDict):
|
@@ -116,30 +120,26 @@ class Runner:
|
|
116
120
|
)
|
117
121
|
|
118
122
|
# Attempt to parse the job yaml file
|
119
|
-
|
120
|
-
|
121
|
-
yml_file = Path(self.source_dir / ".primitive" / f"{self.job['slug']}.yml")
|
123
|
+
job_filename = self.job_settings["repositoryFilename"]
|
124
|
+
logger.info(f"Scanning directory for job file {job_filename}")
|
122
125
|
|
123
|
-
|
124
|
-
logger.error(
|
125
|
-
f"Found two job descriptions with the same slug: {self.job['slug']}"
|
126
|
-
)
|
127
|
-
raise FileExistsError
|
126
|
+
job_config_file = Path(self.source_dir / ".primitive" / job_filename)
|
128
127
|
|
129
|
-
if
|
128
|
+
if job_config_file.exists():
|
130
129
|
logger.info(f"Found job description for {self.job['slug']}")
|
131
|
-
|
132
|
-
self.config = yaml.load(open(config_file, "r"), Loader=Loader)[
|
133
|
-
self.job["name"]
|
134
|
-
]
|
130
|
+
self.config = yaml.load(open(job_config_file, "r"), Loader=Loader)
|
135
131
|
else:
|
136
132
|
logger.error(
|
137
|
-
f"No job description with matching
|
133
|
+
f"No job description with matching filename '{job_filename}' found"
|
138
134
|
)
|
139
135
|
raise FileNotFoundError
|
140
136
|
|
141
137
|
# Setup initial process environment
|
142
138
|
self.initial_env = os.environ
|
139
|
+
self.initial_env = {
|
140
|
+
**self.initial_env,
|
141
|
+
**self.primitive.jobs.get_job_secrets_for_job_run(self.job_run["id"]),
|
142
|
+
}
|
143
143
|
self.initial_env["PRIMITIVE_GIT_SHA"] = str(self.job_run["gitCommit"]["sha"])
|
144
144
|
self.initial_env["PRIMITIVE_GIT_BRANCH"] = str(
|
145
145
|
self.job_run["gitCommit"]["branch"]
|
@@ -261,12 +261,7 @@ class Runner:
|
|
261
261
|
args = [
|
262
262
|
"/bin/bash",
|
263
263
|
"-c",
|
264
|
-
|
265
|
-
f"{cmd} "
|
266
|
-
f"&& stdbuf -oL echo && stdbuf -oL echo '{ENV_VAR_LOOKUP_START}' "
|
267
|
-
"&& env "
|
268
|
-
f"&& stdbuf -oL echo && echo '{ENV_VAR_LOOKUP_END}'"
|
269
|
-
),
|
264
|
+
f"{cmd} && echo -n '{ENV_VAR_LOOKUP_START}' && env && echo -n '{ENV_VAR_LOOKUP_END}'",
|
270
265
|
]
|
271
266
|
|
272
267
|
process = await asyncio.create_subprocess_exec(
|
@@ -310,28 +305,32 @@ class Runner:
|
|
310
305
|
async def log_cmd(self, process, stream, tags: Dict = {}) -> bool:
|
311
306
|
failure_detected = False
|
312
307
|
parse_environment = False
|
313
|
-
|
308
|
+
last_chunk_buffer = b""
|
309
|
+
environment_buffer = b""
|
314
310
|
while chunk := await stream.read(CHUNK_SIZE):
|
315
|
-
|
316
|
-
|
317
|
-
empty_chunk = len(processed_lines) == 0
|
318
|
-
|
319
|
-
if empty_chunk:
|
320
|
-
# Ignore the empty lines
|
311
|
+
if parse_environment:
|
312
|
+
environment_buffer += chunk
|
321
313
|
continue
|
322
|
-
|
323
|
-
|
314
|
+
|
315
|
+
# First, look for start delimiter in chunk
|
316
|
+
full_chunk = last_chunk_buffer + chunk
|
317
|
+
last_chunk_buffer = b""
|
318
|
+
start_index = full_chunk.find(bytes(ENV_VAR_LOOKUP_START, encoding="utf-8"))
|
319
|
+
|
320
|
+
if start_index != -1:
|
321
|
+
environment_buffer = full_chunk[start_index + START_DELIMITER_SIZE :]
|
322
|
+
processed_lines = await self.read_chunk(full_chunk[:start_index])
|
324
323
|
parse_environment = True
|
325
|
-
|
326
|
-
|
327
|
-
# Done reading environment variables
|
328
|
-
parse_environment = False
|
329
|
-
self.modified_env = env_to_dict(env_lines)
|
330
|
-
continue
|
324
|
+
else:
|
325
|
+
processed_lines = await self.read_chunk(full_chunk)
|
331
326
|
|
332
|
-
|
333
|
-
|
334
|
-
|
327
|
+
while (
|
328
|
+
len(last_chunk_buffer) < START_DELIMITER_SIZE
|
329
|
+
and len(processed_lines) > 0
|
330
|
+
):
|
331
|
+
last_chunk_buffer += bytes(
|
332
|
+
processed_lines.pop() + "\n", encoding="utf-8"
|
333
|
+
)
|
335
334
|
|
336
335
|
# Handle logging
|
337
336
|
parse_logs = self.job_settings["parseLogs"]
|
@@ -364,6 +363,16 @@ class Runner:
|
|
364
363
|
|
365
364
|
logger.bind(tag=tag).log(level.value, line)
|
366
365
|
|
366
|
+
start_index = environment_buffer.find(
|
367
|
+
bytes(ENV_VAR_LOOKUP_END, encoding="utf-8")
|
368
|
+
)
|
369
|
+
if parse_environment and start_index == -1:
|
370
|
+
logger.error("Environment variable buffer did not contain end delimiter")
|
371
|
+
failure_detected = True
|
372
|
+
return failure_detected
|
373
|
+
|
374
|
+
environment_buffer = environment_buffer[:start_index]
|
375
|
+
self.modified_env = env_to_dict(environment_buffer)
|
367
376
|
return failure_detected
|
368
377
|
|
369
378
|
async def read_chunk(self, chunk: bytes) -> List[str]:
|
@@ -132,7 +132,7 @@ class LaunchAgent(Daemon):
|
|
132
132
|
<key>ProgramArguments</key>
|
133
133
|
<array>
|
134
134
|
<string>{self.executable}</string>
|
135
|
-
<string>{self.command}
|
135
|
+
{"".join([f"<string>{arg}</string>" for arg in self.command.split(" ") if arg.strip() != ""])}
|
136
136
|
</array>
|
137
137
|
<key>RunAtLoad</key>
|
138
138
|
<true/>
|
primitive/jobs/actions.py
CHANGED
@@ -11,6 +11,7 @@ from .graphql.queries import (
|
|
11
11
|
job_run_query,
|
12
12
|
job_run_status_query,
|
13
13
|
job_runs_query,
|
14
|
+
job_secrets_for_job_run_query,
|
14
15
|
jobs_query,
|
15
16
|
)
|
16
17
|
|
@@ -163,3 +164,12 @@ class Jobs(BaseAction):
|
|
163
164
|
query, variable_values=variables, get_execution_result=True
|
164
165
|
)
|
165
166
|
return result
|
167
|
+
|
168
|
+
@guard
|
169
|
+
def get_job_secrets_for_job_run(self, id: str):
|
170
|
+
query = gql(job_secrets_for_job_run_query)
|
171
|
+
variables = {"jobRunId": id}
|
172
|
+
result = self.primitive.session.execute(
|
173
|
+
query, variable_values=variables, get_execution_result=True
|
174
|
+
)
|
175
|
+
return result.data["jobSecretsForJobRun"]
|
primitive/utils/shell.py
CHANGED
@@ -35,12 +35,14 @@ def add_path_to_shell(path: Path):
|
|
35
35
|
return True
|
36
36
|
|
37
37
|
|
38
|
-
def env_to_dict(env_vars: Union[str, List[str]]) -> Dict:
|
38
|
+
def env_to_dict(env_vars: Union[bytes, str, List[str]]) -> Dict:
|
39
39
|
lines = None
|
40
40
|
if isinstance(env_vars, list):
|
41
41
|
lines = env_vars
|
42
42
|
elif isinstance(env_vars, str):
|
43
43
|
lines = env_vars.splitlines()
|
44
|
+
elif isinstance(env_vars, bytes):
|
45
|
+
lines = env_vars.decode("utf-8").splitlines()
|
44
46
|
else:
|
45
47
|
raise ValueError("Unsupported type. Env_vars must be a list or a string")
|
46
48
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: primitive
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.32
|
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,11 @@
|
|
1
|
-
primitive/__about__.py,sha256=
|
1
|
+
primitive/__about__.py,sha256=oC0l5fABhgagaC-_r-5QZ1VPn7hy8tdJID7wI0iTSow,130
|
2
2
|
primitive/__init__.py,sha256=bwKdgggKNVssJFVPfKSxqFMz4IxSr54WWbmiZqTMPNI,106
|
3
3
|
primitive/cli.py,sha256=g7EtHI9MATAB0qQu5w-WzbXtxz_8zu8z5E7sETmMkKU,2509
|
4
4
|
primitive/client.py,sha256=h8WZVnQylVe0vbpuyC8YZHl2JyITSPC-1HbUcmrE5pc,3623
|
5
5
|
primitive/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
|
-
primitive/agent/actions.py,sha256=
|
6
|
+
primitive/agent/actions.py,sha256=JLnHCtu0AgeHRZccCc2I39mziLixmj9EKtKeYmvBE7A,3700
|
7
7
|
primitive/agent/commands.py,sha256=cK7d3OcN5Z65gQWVZFQ-Y9ddw9Pes4f9OVBpeMsj5sE,255
|
8
|
-
primitive/agent/runner.py,sha256=
|
8
|
+
primitive/agent/runner.py,sha256=e4HBhK16zuhTpKAdKsgoij6G4YjMTn9JQo2YTLRI8i8,15008
|
9
9
|
primitive/agent/uploader.py,sha256=ZzrzsajNBogwEC7mT6Ejy0h2Jd9axMYGzt9pbCvVMlk,3171
|
10
10
|
primitive/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
11
11
|
primitive/auth/actions.py,sha256=9NIEXJ1BNJutJs6AMMSjMN_ziONUAUhY_xHwojYJCLA,942
|
@@ -15,7 +15,7 @@ primitive/auth/graphql/queries.py,sha256=jhrr_VFzHIn8vcVprMIzUx7V4kkWYdR6CKMKPoV
|
|
15
15
|
primitive/daemons/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
16
16
|
primitive/daemons/actions.py,sha256=dqhxgIDxiJlcN6xvkzgBerN215KvbBKKdWteRpT_ICs,2730
|
17
17
|
primitive/daemons/commands.py,sha256=Xt4qFymNrDLdHJhRnEH_4Re-2xX6w1OT-chV9k7dFCs,2670
|
18
|
-
primitive/daemons/launch_agents.py,sha256=
|
18
|
+
primitive/daemons/launch_agents.py,sha256=cCxsvCBFmGlKOiBINsKi_NxcozLC8yPw3w6pxqnz-qI,7803
|
19
19
|
primitive/daemons/launch_service.py,sha256=iuklHeuEqadlf8U1n9xFg4ZG1EKdK2jyaPI-VTlpJ4I,7907
|
20
20
|
primitive/daemons/ui.py,sha256=Af3OJWJ0jdGlb1nfA5yaGYdhBEqqpM8zP2U2vUQdCbw,1236
|
21
21
|
primitive/db/base.py,sha256=mH7f2d_jiyxJSSx9Gk53QBXRa3LiKBsBjkFgvmtH1WA,83
|
@@ -51,12 +51,12 @@ primitive/hardware/graphql/fragments.py,sha256=kI6qnTNjaEaUr-C6eD55COphtueVYbYOW
|
|
51
51
|
primitive/hardware/graphql/mutations.py,sha256=_4Hkbfik9Ron4T-meulu6T-9FR_BZjyPNwn745MPksU,1484
|
52
52
|
primitive/hardware/graphql/queries.py,sha256=I86uLuOSjHSph11Y5MVCYko5Js7hoiEZ-cEoPTc4J-k,1392
|
53
53
|
primitive/jobs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
54
|
-
primitive/jobs/actions.py,sha256=
|
54
|
+
primitive/jobs/actions.py,sha256=Fx2cPc1x09nRasOVtjhPjNRJ-jNoi3RJhXqC3verD9s,5444
|
55
55
|
primitive/jobs/commands.py,sha256=MxPCkBEYW_eLNqgCRYeyj7ZcLOFAWfpVZlqDR2Y_S0o,830
|
56
56
|
primitive/jobs/graphql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
57
|
-
primitive/jobs/graphql/fragments.py,sha256=
|
57
|
+
primitive/jobs/graphql/fragments.py,sha256=pS6jXZ9yxvPKjKx50zpwzARJHYlg7NQLYCm1voiuCzI,642
|
58
58
|
primitive/jobs/graphql/mutations.py,sha256=8ASvCmwQh7cMeeiykOdYaYVryG8FRIuVF6v_J8JJZuw,219
|
59
|
-
primitive/jobs/graphql/queries.py,sha256=
|
59
|
+
primitive/jobs/graphql/queries.py,sha256=ZxNmm-WovytbggNuKRnwa0kc26T34_0yhqkoqx-2uj0,1736
|
60
60
|
primitive/monitor/actions.py,sha256=aYe5OfgCxhapXbcvz7vSlIMAcLOFRcAUWmdBZ8H7UWs,10889
|
61
61
|
primitive/monitor/commands.py,sha256=VDlEL_Qpm_ysHxug7VpI0cVAZ0ny6AS91Y58D7F1zkU,409
|
62
62
|
primitive/organizations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -94,10 +94,10 @@ primitive/utils/daemons.py,sha256=mSoSHitiGfS4KYAEK9sKsiv_YcACHKgY3qISnDpUUIE,10
|
|
94
94
|
primitive/utils/exceptions.py,sha256=DrYHTcCAJGC7cCUwOx_FmdlVLWRdpzvDvpLb82heppE,311
|
95
95
|
primitive/utils/memory_size.py,sha256=4xfha21kW82nFvOTtDFx9Jk2ZQoEhkfXii-PGNTpIUk,3058
|
96
96
|
primitive/utils/printer.py,sha256=f1XUpqi5dkTL3GWvYRUGlSwtj2IxU1q745T4Fxo7Tn4,370
|
97
|
-
primitive/utils/shell.py,sha256=
|
97
|
+
primitive/utils/shell.py,sha256=Z4zxmOaSyGCrS0D6I436iQci-ewHLt4UxVg1CD9Serc,2171
|
98
98
|
primitive/utils/text.py,sha256=XiESMnlhjQ534xE2hMNf08WehE1SKaYFRNih0MmnK0k,829
|
99
|
-
primitive-0.2.
|
100
|
-
primitive-0.2.
|
101
|
-
primitive-0.2.
|
102
|
-
primitive-0.2.
|
103
|
-
primitive-0.2.
|
99
|
+
primitive-0.2.32.dist-info/METADATA,sha256=z-2bverx3L8RAyVNxxYWBv_-3UjdTgfZbk_ety8Swlo,3569
|
100
|
+
primitive-0.2.32.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
101
|
+
primitive-0.2.32.dist-info/entry_points.txt,sha256=p1K8DMCWka5FqLlqP1sPek5Uovy9jq8u51gUsP-z334,48
|
102
|
+
primitive-0.2.32.dist-info/licenses/LICENSE.txt,sha256=B8kmQMJ2sxYygjCLBk770uacaMci4mPSoJJ8WoDBY_c,1098
|
103
|
+
primitive-0.2.32.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|