Skip to content

Commit 3b05d93

Browse files
asmacdoclaude
andcommitted
Backfill OS/distro provenance fields when loading old info.json
Adds a 0.2.3 migration step to ensure_compliant_schema that fills the 14 new system.* provenance fields with empty strings when reading info.json files written by older duct versions, so ls and related consumers can rely on the fields being present. Updates test_ls fixtures to include a "system" block (which has existed in info.json since before MINIMUM_SCHEMA_VERSION) so the new migration can run against them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7255ff5 commit 3b05d93

2 files changed

Lines changed: 59 additions & 3 deletions

File tree

src/con_duct/ls.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,25 @@ def ensure_compliant_schema(info_dict: dict) -> None:
132132
# message field added in 0.2.2
133133
if parse_version(info_dict["schema_version"]) < parse_version("0.2.2"):
134134
info_dict["message"] = ""
135+
# OS and distro provenance fields added to system block in 0.2.3
136+
if parse_version(info_dict["schema_version"]) < parse_version("0.2.3"):
137+
for field in (
138+
"os_name",
139+
"os_release",
140+
"os_version",
141+
"arch",
142+
"processor",
143+
"distro_id",
144+
"distro_id_like",
145+
"distro_name",
146+
"distro_version",
147+
"distro_version_id",
148+
"distro_codename",
149+
"distro_variant_id",
150+
"distro_pretty_name",
151+
"distro_build_id",
152+
):
153+
info_dict["system"][field] = ""
135154

136155

137156
def process_run_data(

test/test_ls.py

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@
2424

2525
def test_load_duct_runs_sanity() -> None:
2626
mock_json = json.dumps(
27-
{"schema_version": "0.2.1", "prefix": "/test/path_", "command": "echo hello"}
27+
{
28+
"schema_version": "0.2.1",
29+
"prefix": "/test/path_",
30+
"command": "echo hello",
31+
"system": {},
32+
}
2833
)
2934
with patch("builtins.open", mock_open(read_data=mock_json)):
3035
result = load_duct_runs(["/test/path_info.json"])
@@ -47,6 +52,7 @@ def test_load_duct_runs_uses_filenames_not_stored_prefix() -> None:
4752
"schema_version": "0.2.1",
4853
"prefix": "/test/not_anymore_",
4954
"command": "echo hello",
55+
"system": {},
5056
}
5157
)
5258
with patch("builtins.open", mock_open(read_data=mock_json)):
@@ -96,10 +102,31 @@ def test_ensure_compliant_schema_noop_for_current_version() -> None:
96102

97103

98104
def test_ensure_compliant_schema_adds_field_for_old_version() -> None:
99-
info: Dict[str, Any] = {"schema_version": "0.2.0", "execution_summary": {}}
105+
info: Dict[str, Any] = {
106+
"schema_version": "0.2.0",
107+
"execution_summary": {},
108+
"system": {},
109+
}
100110
ensure_compliant_schema(info)
101111
assert info["execution_summary"]["working_directory"] == ""
102112
assert info["message"] == ""
113+
for field in (
114+
"os_name",
115+
"os_release",
116+
"os_version",
117+
"arch",
118+
"processor",
119+
"distro_id",
120+
"distro_id_like",
121+
"distro_name",
122+
"distro_version",
123+
"distro_version_id",
124+
"distro_codename",
125+
"distro_variant_id",
126+
"distro_pretty_name",
127+
"distro_build_id",
128+
):
129+
assert info["system"][field] == ""
103130

104131

105132
def test_ensure_compliant_schema_ignores_unexpected_future_version() -> None:
@@ -142,7 +169,12 @@ def test_load_duct_runs_mixed_empty_and_valid_files(
142169
) -> None:
143170
"""Test behavior with mix of empty and valid JSON files."""
144171
valid_json = json.dumps(
145-
{"schema_version": "0.2.1", "prefix": "/test/path_", "command": "echo hello"}
172+
{
173+
"schema_version": "0.2.1",
174+
"prefix": "/test/path_",
175+
"command": "echo hello",
176+
"system": {},
177+
}
146178
)
147179

148180
def side_effect(filename: str) -> Any:
@@ -174,28 +206,33 @@ def setUp(self) -> None:
174206
"file1_info.json": {
175207
"schema_version": MINIMUM_SCHEMA_VERSION,
176208
"execution_summary": {},
209+
"system": {},
177210
"prefix": "test1",
178211
"filter_this": "yes",
179212
},
180213
"file2_info.json": {
181214
"schema_version": MINIMUM_SCHEMA_VERSION,
182215
"execution_summary": {},
216+
"system": {},
183217
"prefix": "test2",
184218
"filter_this": "no",
185219
},
186220
"file3_info.json": {
187221
"schema_version": "0.1.0",
188222
"execution_summary": {},
223+
"system": {},
189224
"prefix": "old_version",
190225
},
191226
"not_matching.json": {
192227
"schema_version": MINIMUM_SCHEMA_VERSION,
193228
"execution_summary": {},
229+
"system": {},
194230
"prefix": "no_match",
195231
},
196232
".duct/logs/default_logpath_info.json": {
197233
"schema_version": MINIMUM_SCHEMA_VERSION,
198234
"execution_summary": {},
235+
"system": {},
199236
"prefix": "default_file1",
200237
},
201238
}

0 commit comments

Comments
 (0)