Last active 1735293001

Revision 89751cd784ced4befb68c5f191674bbbc7a16c0a

compare.sh Raw
1#!/usr/bin/env bash
2#
3# compare_packages.sh
4#
5# Compare package lists from two systems:
6# anduinos-packages.txt
7# ubuntu-24-packages.txt
8
9# Ensure both files exist
10if [[ ! -f "anduinos-packages.txt" || ! -f "ubuntu-24-packages.txt" ]]; then
11 echo "Error: One or both package list files are missing."
12 echo "Please make sure anduinos-packages.txt and ubuntu-24-packages.txt are present."
13 exit 1
14fi
15
16echo "===== Packages installed on anduinos but NOT on ubuntu ====="
17comm -23 <(sort anduinos-packages.txt) <(sort ubuntu-24-packages.txt)
18
19echo
20echo "===== Packages installed on ubuntu but NOT on anduinos ====="
21comm -13 <(sort anduinos-packages.txt) <(sort ubuntu-24-packages.txt)
22
23echo
24echo "Comparison done."
25
export.sh Raw
1dpkg-query -f '${binary:Package}\n' -W > anduinos-packages.txt
2dpkg-query -f '${binary:Package}\n' -W > ubuntu-24-packages.txt
visualize.py Raw
1#!/usr/bin/env python3
2import subprocess
3from collections import defaultdict, deque
4from concurrent.futures import ThreadPoolExecutor
5import threading
6import os
7import sys
8
9def list_installed_packages():
10 """Retrieve a list of all installed packages."""
11 result = subprocess.run(
12 ["dpkg-query", "-f", "${binary:Package}\n", "-W"],
13 stdout=subprocess.PIPE,
14 text=True
15 )
16 return result.stdout.strip().split("\n")
17
18def get_package_dependencies(package):
19 """Query the direct dependencies of a single package."""
20 result = subprocess.run(
21 ["apt-cache", "depends", package],
22 stdout=subprocess.PIPE,
23 text=True
24 )
25 dependencies = []
26 for line in result.stdout.strip().split("\n"):
27 if line.strip().startswith("Depends:"):
28 dep = line.split(":", 1)[1].strip()
29 dep = dep.split(":")[0] # Remove parts after colon
30 dependencies.append(dep)
31 return dependencies
32
33def build_dependency_graph(packages):
34 """Build a dependency graph for the packages and save it to a file."""
35 graph = defaultdict(list)
36 lock = threading.Lock()
37
38 def process_package(package):
39 dependencies = get_package_dependencies(package)
40 with lock:
41 graph[package].extend(dependencies)
42
43 total_packages = len(packages)
44 with ThreadPoolExecutor(max_workers=20) as executor:
45 for i, _ in enumerate(executor.map(process_package, packages), start=1):
46 progress = (i / total_packages) * 100
47 print(f"Building dependency graph... {progress:.2f}% completed", end="\r")
48
49 output_path = "/tmp/pkg.txt"
50 with open(output_path, "w") as f:
51 for package, dependencies in graph.items():
52 for dep in dependencies:
53 f.write(f"{package}-->{dep}\n")
54
55 print(f"\nDependency graph built and saved to {output_path}")
56
57def load_dependency_graph(file_path="/tmp/pkg.txt"):
58 """Load the dependency graph from a file."""
59 if not os.path.exists(file_path):
60 raise FileNotFoundError(f"File {file_path} does not exist. Please run the build mode first.")
61
62 graph = defaultdict(list)
63 reverse_graph = defaultdict(list)
64
65 with open(file_path, "r") as f:
66 for line in f:
67 line = line.strip()
68 if "-->" in line:
69 source, target = line.split("-->")
70 graph[source].append(target)
71 reverse_graph[target].append(source)
72
73 return graph, reverse_graph
74
75def trim_package_name(package):
76 """Trim package name to conform to Mermaid syntax."""
77 return package.replace("-", "_").replace(".", "_").replace("+", "_").replace(":", "_").replace("<", "_").replace(">", "_")
78
79def generate_mermaid_graph(graph, root_package, exclude_leaves=False):
80 """Generate a Mermaid diagram syntax for the graph."""
81 lines = ["stateDiagram-v2"]
82 visited = set()
83 queue = deque([root_package])
84 is_leaf = lambda pkg: len(graph.get(pkg, [])) == 0 # Determine if it is a leaf node
85
86 while queue:
87 package = queue.popleft()
88 if package in visited:
89 continue
90 visited.add(package)
91
92 dependencies = graph.get(package, [])
93 for dep in dependencies:
94 if exclude_leaves and is_leaf(dep):
95 continue # Skip leaf nodes
96
97 lines.append(f" {trim_package_name(package)} --> {trim_package_name(dep)}")
98 if dep not in visited:
99 queue.append(dep)
100
101 return "\n".join(lines)
102
103def build_mode():
104 print("Retrieving installed packages...")
105 packages = list_installed_packages()
106 print("Building dependency graph...")
107 build_dependency_graph(packages)
108
109def depends_mode(package, exclude_leaves):
110 graph, _ = load_dependency_graph()
111 if package not in graph:
112 print(f"Package {package} is not in the dependency graph.")
113 return
114
115 print("Generating dependency graph...")
116 mermaid_graph = generate_mermaid_graph(graph, package, exclude_leaves)
117
118 with open(f"{package}_depends.mmd", "w") as f:
119 f.write("---\n")
120 f.write(f"title: {package} Dependency Graph\n")
121 f.write("---\n\n")
122 f.write(mermaid_graph)
123
124 print(f"Dependency graph generated and saved as {package}_depends.mmd")
125
126def rdepends_mode(package, exclude_leaves):
127 _, reverse_graph = load_dependency_graph()
128 if package not in reverse_graph:
129 print(f"Package {package} is not in the reverse dependency graph.")
130 return
131
132 print("Generating reverse dependency graph...")
133 mermaid_graph = generate_mermaid_graph(reverse_graph, package, exclude_leaves)
134
135 with open(f"{package}_rdepends.mmd", "w") as f:
136 f.write("---\n")
137 f.write(f"title: {package} Reverse Dependency Graph\n")
138 f.write("---\n\n")
139 f.write(mermaid_graph)
140
141 print(f"Reverse dependency graph generated and saved as {package}_rdepends.mmd")
142
143def main():
144 if len(sys.argv) < 2:
145 print("Usage: ./vispkg.py [build|depends|rdepends] [package] [--no-leaves]")
146 sys.exit(1)
147
148 mode = sys.argv[1]
149 exclude_leaves = "--no-leaves" in sys.argv
150
151 if mode == "build":
152 build_mode()
153 elif mode == "depends":
154 if len(sys.argv) < 3:
155 print("Usage: ./vispkg.py depends <package> [--no-leaves]")
156 sys.exit(1)
157 depends_mode(sys.argv[2], exclude_leaves)
158 elif mode == "rdepends":
159 if len(sys.argv) < 3:
160 print("Usage: ./vispkg.py rdepends <package> [--no-leaves]")
161 sys.exit(1)
162 rdepends_mode(sys.argv[2], exclude_leaves)
163 else:
164 print("Unknown mode. Please use: build, depends, or rdepends.")
165 sys.exit(1)
166
167if __name__ == "__main__":
168 main()
169