File: run_workflows_for_branch.py

package info (click to toggle)
onnxruntime 1.23.2%2Bdfsg-6
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 340,756 kB
  • sloc: cpp: 3,222,136; python: 188,267; ansic: 114,318; asm: 37,927; cs: 36,849; java: 10,962; javascript: 6,811; pascal: 4,126; sh: 2,996; xml: 705; objc: 281; makefile: 67
file content (149 lines) | stat: -rw-r--r-- 5,437 bytes parent folder | download
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#!/usr/bin/env python3
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.
import argparse
import os
import subprocess

import yaml


def get_repo_root():
    result = subprocess.run(["git", "rev-parse", "--show-toplevel"], capture_output=True, text=True, check=False)
    if result.returncode != 0:
        raise RuntimeError("❌ Not inside a Git repository.")
    return result.stdout.strip()


def get_current_branch():
    result = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"], capture_output=True, text=True, check=False)
    return result.stdout.strip()


def get_dispatchable_workflows(
    workflows_dir: str, include: list[str] | None = None, exclude: list[str] | None = None
) -> list[tuple[str, str]]:
    dispatchable = []
    for file in os.listdir(workflows_dir):
        if not file.endswith(".yml"):
            continue

        filepath = os.path.join(workflows_dir, file)
        with open(filepath, encoding="utf-8") as f:
            try:
                content = yaml.safe_load(f)
                triggers = content.get("on") or content.get(True)
                name = content.get("name")
                is_dispatchable = False
                if name and triggers:
                    if isinstance(triggers, dict) and "workflow_dispatch" in triggers:
                        is_dispatchable = True
                    elif isinstance(triggers, list) and "workflow_dispatch" in triggers:
                        is_dispatchable = True
                    elif isinstance(triggers, str) and triggers == "workflow_dispatch":
                        is_dispatchable = True

                    if is_dispatchable:
                        add = not include  # true unless there's an include filter
                        if include and any(inc.lower() in name.lower() for inc in include):
                            add = True

                        if exclude and any(exc.lower() in name.lower() for exc in exclude):
                            add = False

                        if add:
                            dispatchable.append((name, file))
            except yaml.YAMLError as e:
                print(f"⚠️ Failed to parse {file}: {e}")
    return dispatchable


def trigger_workflow(name: str, file_name: str, branch: str, dry_run: bool = False):
    command = ["gh", "workflow", "run", file_name, "--ref", branch]
    print(f"Workflow:{name}\n\t{' '.join(command)}")

    if dry_run:
        return

    result = subprocess.run(command, capture_output=True, text=True, check=False)
    if result.returncode == 0:
        print(f"✅ Triggered {file_name} on branch {branch}")
    else:
        print(f"❌ Failed to trigger {file_name}:\n{result.stderr}")


class DefaultArgsRawHelpFormatter(argparse.ArgumentDefaultsHelpFormatter, argparse.RawDescriptionHelpFormatter):
    pass


def _parse_args():
    parser = argparse.ArgumentParser(
        os.path.basename(__file__),
        formatter_class=DefaultArgsRawHelpFormatter,
        description="""Run the GitHub workflows that have workflow_dispatch enabled for a branch.

        If specified, the `--include` filter is applied first, followed by any `--exclude` filter.
        `--include` and `--exclude` can be specified multiple times to accumulate values to include/exclude.

        Requires the GitHub CLI to be installed.

        Example usage:
          List all workflows that can be triggered.
            `python run_workflows_for_branch.py --dry-run [my/BranchName]`
          Run all workflows.
            `python run_workflows_for_branch.py [my/BranchName]`
          Run only Linux CIs
            `python run_workflows_for_branch.py --include linux [my/BranchName]`
          Exclude training CIs
            `python run_workflows_for_branch.py --exclude training [my/BranchName]`
          Run non-training Linux CIs
            `python run_workflows_for_branch.py --include linux --exclude training [my/BranchName]`
        """,
    )

    current_branch = get_current_branch()

    parser.add_argument(
        "-i", "--include", action="append", type=str, help="Include workflows that match this string. Case insensitive."
    )
    parser.add_argument(
        "-e", "--exclude", action="append", type=str, help="Exclude workflows that match this string. Case insensitive."
    )
    parser.add_argument("--dry-run", action="store_true", help="Print selected workflows but do not run them.")
    parser.add_argument(
        "branch",
        type=str,
        nargs="?",
        default=current_branch,
        help="Specify the branch to run. Default is current branch if available.",
    )

    args = parser.parse_args()
    if not args.branch:
        raise ValueError("Branch was unable to be inferred and must be specified")

    return args


def main():
    args = _parse_args()

    repo_root = get_repo_root()
    workflows_dir = os.path.join(repo_root, ".github", "workflows")

    print(f"Branch: {args.branch}")

    workflows = get_dispatchable_workflows(workflows_dir, args.include, args.exclude)
    if not workflows:
        print("⚠️ No dispatchable workflows found.")
        return

    print(f"🔍 Found {len(workflows)} dispatchable workflows:")
    for wf in workflows:
        name = wf[0]
        file_name = wf[1]
        trigger_workflow(name, file_name, args.branch, args.dry_run)


if __name__ == "__main__":
    main()