-
Notifications
You must be signed in to change notification settings - Fork 11
/
diff.py
112 lines (89 loc) · 2.66 KB
/
diff.py
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import io
import json
import sys
from typing import Any, Generator
import git
def is_same(d1: dict[str, Any], d2: dict[str, Any]) -> bool:
"""
Are d1/d2 the 'same' as far as deployed IOCs are concerned?
Parameters
----------
d1 : dict[str, Any]
d2 : dict[str, Any]
Returns
-------
bool
"""
if set(d1) != set(d2):
return False
return all(str(d1[k]) == str(d2[k]) for k in d1)
def get_changes(
d1: dict[str, Any], d2: dict[str, Any]
) -> Generator[tuple[str, str, Any], None, None]:
"""
Get changes between d1 -> d2.
Parameters
----------
d1 : dict[str, Any]
d2 : dict[str, Any]
Yields
------
str
added/deleted/changed
str
The key name
Any
The new/updated value
"""
for key in set(d2) - set(d1):
yield "added", key, d2[key]
for key in set(d1) - set(d2):
yield "deleted", key, None
for key in d2:
if key in d1 and d1[key] != d2[key]:
yield "changed", key, d2[key]
def get_database_from_commit(commit_hash: str) -> dict[str, Any]:
"""
Given a commit hash, get the (deserialized) happi database.
Parameters
----------
commit_hash : str
The commit hash.
Returns
-------
dict[str, Any]
The loaded happi database.
"""
blob = git.Repo(".").commit(commit_hash).tree / "db.json"
with io.BytesIO() as fp:
blob.stream_data(fp)
return json.loads(fp.getvalue().decode("utf-8"))
def print_changes(hash1: str, hash2: str) -> None:
"""
Print the changes made to the database between the two hashes.
"""
db1, db2 = [get_database_from_commit(sha) for sha in (hash1, hash2)]
device_changes = sorted(get_changes(db1, db2))
print("## Summary")
print(f"Device changes {len(device_changes)} of total devices {len(db2)}")
for action, device, _ in device_changes:
print()
print(f"## {device}: {action}")
if action == "deleted":
continue
dev1 = db1.get(device, {})
dev2 = db2.get(device, {})
for action, key, value in sorted(get_changes(dev1, dev2)):
if key == "last_edit":
continue
if action == "deleted":
print(f"* ``{key!r}`` deleted")
elif action == "added":
print(f"* ``{key}``: ``{value!r}``")
else:
old_value = dev1.get(key, "(no value)")
new_value = dev2.get(key, "(no value)")
print(f"* {key}: ``{old_value!r}`` -> ``{new_value!r}``")
if __name__ == "__main__":
hash1, hash2 = sys.argv[1:3]
print_changes(hash1, hash2)