Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
- cron: "0 10 * * *"
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 "therockbot@amd.com"
- 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

96 changes: 93 additions & 3 deletions build_tools/bump_submodules.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,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 +62,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 @@ -131,6 +193,9 @@ def run(args: argparse.Namespace, fetch_args: list[str], system_projects: list[s
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
else:
Expand Down Expand Up @@ -170,7 +235,32 @@ def run(args: argparse.Namespace, fetch_args: list[str], system_projects: list[s
["git", "push", "-u", "origin", args.branch_name],
cwd=THEROCK_DIR,
)

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("WARNING: No submodule hash changes detected for the component.")
pr_title = f"Bump {component} from unknown to unknown"
else:
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"
pr_body += "### Submodule changes:\n"
for sm in new_hashes:
pr_body += f"- `{sm}`: {old_hashes.get(sm)} → {new_hashes.get(sm)}\n"
# Create PR
exec(
[
"gh", "pr", "create",
"--title", pr_title,
"--body", pr_body,
"--head", args.branch_name,
"--base", "main",
"--reviewer", "ScottTodd",
"--reviewer", "marbre",
"--reviewer", "geomin12",
"--reviewer", "jayhawk-commits",
],
cwd=THEROCK_DIR,
)

def main(argv):
parser = argparse.ArgumentParser(prog="bump_submodules")
Expand Down Expand Up @@ -220,4 +310,4 @@ def main(argv):


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