Skip to content

Instantly share code, notes, and snippets.

@0xack13
Created January 29, 2025 22:21
Show Gist options
  • Save 0xack13/6aa3f2a491ffd703995817cbf6a8d980 to your computer and use it in GitHub Desktop.
Save 0xack13/6aa3f2a491ffd703995817cbf6a8d980 to your computer and use it in GitHub Desktop.
gha_map
import os
import yaml
from pathlib import Path
import argparse
def find_called_workflows(file_path):
"""Parse a workflow file to find referenced workflows."""
called_workflows = set()
with open(file_path, "r") as f:
try:
data = yaml.safe_load(f)
if not isinstance(data, dict):
return called_workflows
jobs = data.get("jobs", {})
for job in jobs.values():
if isinstance(job, dict):
steps = job.get("steps", [])
for step in steps:
if isinstance(step, dict):
uses = step.get("uses")
if uses and uses.startswith("./.github/workflows/"):
called_workflows.add(uses.replace("./", ""))
except yaml.YAMLError:
pass
return called_workflows
def build_workflow_tree(directory):
"""Build a tree of workflows and their dependencies."""
workflow_tree = {}
for workflow_file in Path(directory).glob("*.yml") | Path(directory).glob("*.yaml"):
workflow_tree[workflow_file.name] = find_called_workflows(workflow_file)
return workflow_tree
def print_tree(tree, root, prefix=""):
"""Recursively print the workflow tree."""
print(prefix + "├── " + root)
children = tree.get(root, [])
for idx, child in enumerate(children):
new_prefix = prefix + ("│ " if idx < len(children) - 1 else " ")
print_tree(tree, child, new_prefix)
def main():
parser = argparse.ArgumentParser(description="Visualize GitHub Actions workflow dependencies.")
parser.add_argument("-r", "--root", help="Specify a workflow file as the root", default=None)
args = parser.parse_args()
workflows_dir = Path(".github/workflows")
if not workflows_dir.exists():
print("No workflows directory found.")
return
workflow_tree = build_workflow_tree(workflows_dir)
if args.root:
if args.root not in workflow_tree:
print(f"Specified root '{args.root}' not found in workflows.")
return
print_tree(workflow_tree, args.root)
else:
roots = set(workflow_tree.keys()) - {w for deps in workflow_tree.values() for w in deps}
for root in sorted(roots):
print_tree(workflow_tree, root)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment