compare.sh
· 718 B · Bash
Raw
#!/usr/bin/env bash
#
# compare_packages.sh
#
# Compare package lists from two systems:
# anduinos-packages.txt
# ubuntu-24-packages.txt
# Ensure both files exist
if [[ ! -f "anduinos-packages.txt" || ! -f "ubuntu-24-packages.txt" ]]; then
echo "Error: One or both package list files are missing."
echo "Please make sure anduinos-packages.txt and ubuntu-24-packages.txt are present."
exit 1
fi
echo "===== Packages installed on anduinos but NOT on ubuntu ====="
comm -23 <(sort anduinos-packages.txt) <(sort ubuntu-24-packages.txt)
echo
echo "===== Packages installed on ubuntu but NOT on anduinos ====="
comm -13 <(sort anduinos-packages.txt) <(sort ubuntu-24-packages.txt)
echo
echo "Comparison done."
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 |
10 | if [[ ! -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 |
14 | fi |
15 | |
16 | echo "===== Packages installed on anduinos but NOT on ubuntu =====" |
17 | comm -23 <(sort anduinos-packages.txt) <(sort ubuntu-24-packages.txt) |
18 | |
19 | echo |
20 | echo "===== Packages installed on ubuntu but NOT on anduinos =====" |
21 | comm -13 <(sort anduinos-packages.txt) <(sort ubuntu-24-packages.txt) |
22 | |
23 | echo |
24 | echo "Comparison done." |
25 |
1 | dpkg-query -f '${binary:Package}\n' -W > anduinos-packages.txt |
2 | dpkg-query -f '${binary:Package}\n' -W > ubuntu-24-packages.txt |
visualize.py
· 5.6 KiB · Python
Raw
#!/usr/bin/env python3
import subprocess
from collections import defaultdict, deque
from concurrent.futures import ThreadPoolExecutor
import threading
import os
import sys
def list_installed_packages():
"""Retrieve a list of all installed packages."""
result = subprocess.run(
["dpkg-query", "-f", "${binary:Package}\n", "-W"],
stdout=subprocess.PIPE,
text=True
)
return result.stdout.strip().split("\n")
def get_package_dependencies(package):
"""Query the direct dependencies of a single package."""
result = subprocess.run(
["apt-cache", "depends", package],
stdout=subprocess.PIPE,
text=True
)
dependencies = []
for line in result.stdout.strip().split("\n"):
if line.strip().startswith("Depends:"):
dep = line.split(":", 1)[1].strip()
dep = dep.split(":")[0] # Remove parts after colon
dependencies.append(dep)
return dependencies
def build_dependency_graph(packages):
"""Build a dependency graph for the packages and save it to a file."""
graph = defaultdict(list)
lock = threading.Lock()
def process_package(package):
dependencies = get_package_dependencies(package)
with lock:
graph[package].extend(dependencies)
total_packages = len(packages)
with ThreadPoolExecutor(max_workers=20) as executor:
for i, _ in enumerate(executor.map(process_package, packages), start=1):
progress = (i / total_packages) * 100
print(f"Building dependency graph... {progress:.2f}% completed", end="\r")
output_path = "/tmp/pkg.txt"
with open(output_path, "w") as f:
for package, dependencies in graph.items():
for dep in dependencies:
f.write(f"{package}-->{dep}\n")
print(f"\nDependency graph built and saved to {output_path}")
def load_dependency_graph(file_path="/tmp/pkg.txt"):
"""Load the dependency graph from a file."""
if not os.path.exists(file_path):
raise FileNotFoundError(f"File {file_path} does not exist. Please run the build mode first.")
graph = defaultdict(list)
reverse_graph = defaultdict(list)
with open(file_path, "r") as f:
for line in f:
line = line.strip()
if "-->" in line:
source, target = line.split("-->")
graph[source].append(target)
reverse_graph[target].append(source)
return graph, reverse_graph
def trim_package_name(package):
"""Trim package name to conform to Mermaid syntax."""
return package.replace("-", "_").replace(".", "_").replace("+", "_").replace(":", "_").replace("<", "_").replace(">", "_")
def generate_mermaid_graph(graph, root_package, exclude_leaves=False):
"""Generate a Mermaid diagram syntax for the graph."""
lines = ["stateDiagram-v2"]
visited = set()
queue = deque([root_package])
is_leaf = lambda pkg: len(graph.get(pkg, [])) == 0 # Determine if it is a leaf node
while queue:
package = queue.popleft()
if package in visited:
continue
visited.add(package)
dependencies = graph.get(package, [])
for dep in dependencies:
if exclude_leaves and is_leaf(dep):
continue # Skip leaf nodes
lines.append(f" {trim_package_name(package)} --> {trim_package_name(dep)}")
if dep not in visited:
queue.append(dep)
return "\n".join(lines)
def build_mode():
print("Retrieving installed packages...")
packages = list_installed_packages()
print("Building dependency graph...")
build_dependency_graph(packages)
def depends_mode(package, exclude_leaves):
graph, _ = load_dependency_graph()
if package not in graph:
print(f"Package {package} is not in the dependency graph.")
return
print("Generating dependency graph...")
mermaid_graph = generate_mermaid_graph(graph, package, exclude_leaves)
with open(f"{package}_depends.mmd", "w") as f:
f.write("---\n")
f.write(f"title: {package} Dependency Graph\n")
f.write("---\n\n")
f.write(mermaid_graph)
print(f"Dependency graph generated and saved as {package}_depends.mmd")
def rdepends_mode(package, exclude_leaves):
_, reverse_graph = load_dependency_graph()
if package not in reverse_graph:
print(f"Package {package} is not in the reverse dependency graph.")
return
print("Generating reverse dependency graph...")
mermaid_graph = generate_mermaid_graph(reverse_graph, package, exclude_leaves)
with open(f"{package}_rdepends.mmd", "w") as f:
f.write("---\n")
f.write(f"title: {package} Reverse Dependency Graph\n")
f.write("---\n\n")
f.write(mermaid_graph)
print(f"Reverse dependency graph generated and saved as {package}_rdepends.mmd")
def main():
if len(sys.argv) < 2:
print("Usage: ./vispkg.py [build|depends|rdepends] [package] [--no-leaves]")
sys.exit(1)
mode = sys.argv[1]
exclude_leaves = "--no-leaves" in sys.argv
if mode == "build":
build_mode()
elif mode == "depends":
if len(sys.argv) < 3:
print("Usage: ./vispkg.py depends <package> [--no-leaves]")
sys.exit(1)
depends_mode(sys.argv[2], exclude_leaves)
elif mode == "rdepends":
if len(sys.argv) < 3:
print("Usage: ./vispkg.py rdepends <package> [--no-leaves]")
sys.exit(1)
rdepends_mode(sys.argv[2], exclude_leaves)
else:
print("Unknown mode. Please use: build, depends, or rdepends.")
sys.exit(1)
if __name__ == "__main__":
main()
1 | #!/usr/bin/env python3 |
2 | import subprocess |
3 | from collections import defaultdict, deque |
4 | from concurrent.futures import ThreadPoolExecutor |
5 | import threading |
6 | import os |
7 | import sys |
8 | |
9 | def 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 | |
18 | def 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 | |
33 | def 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 | |
57 | def 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 | |
75 | def trim_package_name(package): |
76 | """Trim package name to conform to Mermaid syntax.""" |
77 | return package.replace("-", "_").replace(".", "_").replace("+", "_").replace(":", "_").replace("<", "_").replace(">", "_") |
78 | |
79 | def 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 | |
103 | def build_mode(): |
104 | print("Retrieving installed packages...") |
105 | packages = list_installed_packages() |
106 | print("Building dependency graph...") |
107 | build_dependency_graph(packages) |
108 | |
109 | def 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 | |
126 | def 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 | |
143 | def 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 | |
167 | if __name__ == "__main__": |
168 | main() |
169 |