Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/bump_rocm_libraries.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: Bump rocm-libraries submodule

on:
workflow_dispatch:
schedule:
# 2:00 AM PST → 10:00 UTC, Monday–Friday
- cron: "0 10 * * 1-5"
permissions:
contents: write
pull-requests: write
jobs:
bump-submodules:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0
with:
python-version: '3.12'
- name: Configure Git Identity
run: |
git config --global user.name "therockbot"
git config --global user.email "[email protected]"

- name: Set branch name
id: set-branch
run: |
DATE=$(date +%Y%m%d)
BRANCH="github-action/bump-rocm-libraries-submodule-$DATE"
echo "branch-name=$BRANCH" >> $GITHUB_OUTPUT

- name: Generate GitHub App token
uses: actions/create-github-app-token@7e473efe3cb98aa54f8d4bac15400b15fad77d94 # v2.2.0
id: generate-token
with:
app-id: ${{ secrets.PULL_REQUEST_APP_ID }}
private-key: ${{ secrets.PULL_REQUEST_APP_KEY }}

- name: Run bump_submodules.py
env:
GH_TOKEN: ${{ steps.generate-token.outputs.token }}
run: |
python3 ./build_tools/bump_submodules.py --push-branch \
--branch-name ${{ steps.set-branch.outputs.branch-name }} \
--components rocm-libraries"
Comment on lines +41 to +44
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing " on the last line here? Remove?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was waiting on the final go ahead to fix all the pre-commit issues.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The final go will be the approval but we cannot approve before this wasn't addressed thus not sure what you're looking for.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a syntax error here. Did you test the workflow (on your fork)?

pre-commit is for formatting issues. You should install the pre-commit git hook or otherwise run pre-commit manually yourself. We should never see pre-commit failures on PRs if you have that properly configured.

See https://github.com/ROCm/TheRock/blob/main/CONTRIBUTING.md#pre-commit-checks

127 changes: 111 additions & 16 deletions build_tools/bump_submodules.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"""Helper script to bump TheRock's submodules, doing the following:
* (Optional) Creates a new branch
* Updates submodules from remote using `fetch_sources.py`
* Creares a commit and tries to apply local patches
* Creates a commit and tries to apply local patches
* (Optional) Pushed the new branch to origin

* (Optional) Create pull request from the new branch to origin (requires gh cli installed)
The submodules to bump can be specified via `--components`.

Examples:
Expand All @@ -27,6 +27,7 @@
import shlex
import subprocess
import sys
import shutil

THIS_SCRIPT_DIR = Path(__file__).resolve().parent
THEROCK_DIR = THIS_SCRIPT_DIR.parent
Expand All @@ -36,7 +37,6 @@ def log(*args, **kwargs):
print(*args, **kwargs)
sys.stdout.flush()


def exec(args: list[str | Path], cwd: Path):
args = [str(arg) for arg in args]
log(f"++ Exec [{cwd}]$ {shlex.join(args)}")
Expand All @@ -63,6 +63,69 @@ def pin_ck():
cwd=THEROCK_DIR / "ml-libs" / "composable_kernel",
)

def get_component_submodules(component: str) -> list[str]:
"""
Returns the list of submodule paths belonging to the component.
Reads .gitmodules to ensure accuracy.
"""

gitmodules_path = THEROCK_DIR / ".gitmodules"
content = gitmodules_path.read_text()

paths = []

for line in content.splitlines():
line = line.strip()
if line.startswith("path = "):
sub_path = line.split("=", 1)[1].strip()
if sub_path == component or sub_path.startswith(component + "/"):
paths.append(sub_path)
return paths


def get_submodule_hash(path: str) -> str:
"""
Returns the commit hash for a submodule path.
"""
output = subprocess.check_output(
["git", "submodule", "status", path],
cwd=str(THEROCK_DIR),
text=True
).strip()

commit = output.split()[0].lstrip("+ -")
return commit


def get_hashes_for_component(component: str) -> dict:
"""
Returns {submodule_path: hash} for the component.
"""
submodules = get_component_submodules(component)

hashes = {}
for sm in submodules:
try:
h = get_submodule_hash(sm)
hashes[sm] = h
except Exception as e:
log(f"Failed to read hash for {sm}: {e}")
return hashes


def detect_hash_change(old: dict, new: dict):
"""
Return (old_hash, new_hash, path) for the first changed submodule.
"""
for path in new:
old_h = old.get(path)
new_h = new[path]
if old_h != new_h:
log(f"CHANGE DETECTED in {path}: {old_h} -> {new_h}")
return old_h, new_h, path

log("No changes detected for component")
return None, None, None

def parse_components(components: list[str]) -> list[list]:
arguments = []
Expand Down Expand Up @@ -123,13 +186,8 @@ def parse_components(components: list[str]) -> list[list]:


def run(args: argparse.Namespace, fetch_args: list[str], system_projects: list[str]):
date = datetime.today().strftime("%Y%m%d")

if args.create_branch or args.push_branch:
exec(
["git", "checkout", "-b", args.branch_name],
cwd=THEROCK_DIR,
)
component = args.components[0]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not confident this works. This only takes the first component but ignores if a user passes a list of components. While this is acceptable for the intended use case, it breaks the script when using it to bump more than one component as far as I see. Either this needs to be addressed properly or the changes for automation must go to it's own script.

old_hashes = get_hashes_for_component(component)

if system_projects:
projects_args = ["--system-projects"] + system_projects
Expand All @@ -151,10 +209,28 @@ def run(args: argparse.Namespace, fetch_args: list[str], system_projects: list[s
if args.pin_ck:
pin_ck()

exec(
["git", "commit", "-a", "-m", "Bump submodules " + date],
cwd=THEROCK_DIR,
# Detect new hashes BEFORE commit/branch creation
new_hashes = get_hashes_for_component(component)
old_h, new_h, changed_path = detect_hash_change(old_hashes, new_hashes)
if not old_h or not new_h:
log("No submodule changes detected – aborting without commit, branch, or PR.")
return

if args.create_branch or args.push_branch or args.create_pr:
exec(["git", "checkout", "-b", args.branch_name], cwd=THEROCK_DIR)

# Prepare commit/PR title/body
pr_title = f"Bump {component} from {old_h[:7]} to {new_h[:7]}"
pr_body = (
f"Bump happened on {datetime.today().strftime('%m%d%Y')}\n\n"
"### Submodule changes:\n"
+ "".join(
f"- `{sm}`: {old_hashes.get(sm)} → {new_hashes.get(sm)}\n"
for sm in new_hashes
)
)
commit_msg = pr_title + "\n\n" + pr_body
exec(["git", "commit", "-a", "-m", commit_msg], cwd=THEROCK_DIR)

try:
exec(
Expand All @@ -169,8 +245,21 @@ def run(args: argparse.Namespace, fetch_args: list[str], system_projects: list[s
exec(
["git", "push", "-u", "origin", args.branch_name],
cwd=THEROCK_DIR,
)

)
# Create PR
if args.create_pr:
if not shutil.which("gh"):
raise SystemExit("ERROR: --create-pr requires the `gh` CLI.\n")
Comment on lines +251 to +252
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eventually check earlier and before creating the commit and pushing the branch?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes move that to argument parsing, like here:

args = parser.parse_args()
# Check preconditions for provided arguments before proceeding.
if args.upload:
if not args.run_id:
parser.error("when --upload is true, --run_id must also be set")
if not shutil.which("aws"):
raise FileNotFoundError(
"AWS CLI 'aws' not found on PATH, uploading requires it"
)

We can add an example like that to the style guide if it would help: https://github.com/ROCm/TheRock/blob/main/docs/development/style_guide.md#use-argparse-for-cli-flags

exec(
[
"gh", "pr", "create",
"--title", pr_title,
"--body", pr_body,
"--head", args.branch_name,
"--base", "main",
],
cwd=THEROCK_DIR,
)

def main(argv):
parser = argparse.ArgumentParser(prog="bump_submodules")
Expand Down Expand Up @@ -214,10 +303,16 @@ def main(argv):
profiler
""",
)
parser.add_argument(
"--create-pr",
default=False,
action=argparse.BooleanOptionalAction,
help="Create a GitHub PR (requires `gh` CLI)",
)
args = parser.parse_args(argv)
fetch_args, system_projects = parse_components(args.components)
run(args, fetch_args, system_projects)


if __name__ == "__main__":
main(sys.argv[1:])
main(sys.argv[1:])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing newline.

Loading