compare.sh
· 718 B · Bash
Originalformat
#!/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 |
export.sh
· 126 B · Bash
Originalformat
dpkg-query -f '${binary:Package}\n' -W > anduinos-packages.txt
dpkg-query -f '${binary:Package}\n' -W > ubuntu-24-packages.txt
| 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
Originalformat
#!/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 |