artifact_write.py
3.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
"""c_artifact_write (03-C §3.7): the only writer of `artifact` rows.
Flagged as an unowned gap when scoping U-011 (REQ-02's happy AND failure
cases both need it directly, and no Unit in development-plan.md's 43-unit
breakdown built it). Built now because U-014 (end-to-end acceptance) is the
first Unit that actually needs artifacts to exist for real, not seeded.
Simplification, noted rather than silently assumed: `validation_status` is
set to 'passed' unconditionally at write time. REQ-02's boundary case
("PRD 中出现互相矛盾的场景假设时,一致性校验应在 verifying 态被拦截") describes
semantic content review, which would need its own LLM-judgment call this
codebase doesn't build yet (distinct from work_node_execution's schema-shape
validation, already real since U-013) -- out of scope here, same as every
other REQ boundary/failure case this session has deferred rather than faked.
"""
import json
import os
import subprocess
import uuid
_GIT_IDENTITY = ["-c", "user.name=graphlemon", "-c", "user.email=graphlemon@localhost"]
class InvalidDependencyError(Exception):
"""REQ-02 failure case: depends_on_artifact_ids references a nonexistent artifact_id."""
class GitCommitFailedError(Exception):
"""GIT_COMMIT_FAILED -- 03-C §3.7."""
def write_artifact(
conn, target_folder_path, artifact_type, task_id, produced_by_run_id,
file_path, content, depends_on_artifact_ids=None,
):
"""Writes `content` to `<target_folder_path>/<file_path>`, commits it to
the target project's own git history (not graphlemon's -- 01-M 1.5:
artifact.git_commit_sha is the *target project's* commit), and creates
the immutable `artifact` row. Returns {"artifact_id", "git_commit_sha"}.
"""
depends_on_artifact_ids = depends_on_artifact_ids or []
for dep_id in depends_on_artifact_ids:
if conn.execute("SELECT 1 FROM artifact WHERE artifact_id = ?", (dep_id,)).fetchone() is None:
raise InvalidDependencyError("depends_on_artifact_ids references nonexistent artifact: " + dep_id)
abs_path = os.path.join(target_folder_path, file_path)
os.makedirs(os.path.dirname(abs_path), exist_ok=True)
with open(abs_path, "w", encoding="utf-8") as f:
f.write(content)
try:
subprocess.run(
["git", "add", file_path], cwd=target_folder_path, check=True, capture_output=True, text=True
)
subprocess.run(
["git"] + _GIT_IDENTITY + [
"commit", "-m", "graphlemon: {} artifact for task {}".format(artifact_type, task_id)
],
cwd=target_folder_path, check=True, capture_output=True, text=True,
)
sha = subprocess.run(
["git", "rev-parse", "HEAD"], cwd=target_folder_path, check=True, capture_output=True, text=True
).stdout.strip()
except subprocess.CalledProcessError as e:
raise GitCommitFailedError(str(e)) from e
artifact_id = str(uuid.uuid4())
conn.execute(
"INSERT INTO artifact "
"(artifact_id, artifact_type, task_id, produced_by_run_id, file_path, git_commit_sha, "
"depends_on_artifact_ids, validation_status) VALUES (?, ?, ?, ?, ?, ?, ?, 'passed')",
(
artifact_id, artifact_type, task_id, produced_by_run_id, file_path, sha,
json.dumps(depends_on_artifact_ids),
),
)
conn.commit()
return {"artifact_id": artifact_id, "git_commit_sha": sha}