Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
93efa13
[ci] Auto-generate manifest-diff report from multi-arch CI on PR/push…
amd-hsivasun May 12, 2026
d9d724c
[docs] Document manifest-diff report tool and multi-arch CI integration
amd-hsivasun May 12, 2026
63eb988
[CI] Add --branch parameter into manifest-diff workflow
amd-hsivasun May 12, 2026
c327453
[Docs] Link manifest-diff doc to new issue created
amd-hsivasun May 12, 2026
ede586c
Merge branch 'main' into amd/hsivasun/bump-pr-blamelist
amd-hsivasun May 12, 2026
1d9f400
[CI] Move manifest_diff to multi_arch_ci.yml; convert manifest-diff.yml
amd-hsivasun May 14, 2026
04cb776
Merge branch 'main' into amd/hsivasun/bump-pr-blamelist
amd-hsivasun May 14, 2026
ec0fd68
[CI] Add safe.directory step to manifest-diff for container-based git…
amd-hsivasun May 15, 2026
aa6113d
Merge branch 'main' into amd/hsivasun/bump-pr-blamelist
amd-hsivasun May 15, 2026
ebd3930
Update manifest_diff.md
amd-hsivasun May 19, 2026
98c08b3
[CI] Tighten manifest-diff input descriptions and align help/docstring
amd-hsivasun May 20, 2026
ee148fc
[CI] manifest-diff: install ca-certificates in ubuntu:24.04 container
amd-hsivasun May 20, 2026
125f8ba
[CI] manifest-diff: also install git for submodule walks
amd-hsivasun May 20, 2026
cc3c0e2
[CI] manifest-diff: bootstrap container before checkout; install AWS …
amd-hsivasun May 20, 2026
d4e8015
[CI] manifest-diff: persist safe.directory and consolidate system req…
amd-hsivasun May 20, 2026
b64b68e
[CI] manifest-diff: derive refs from caller github.event; drop with: …
amd-hsivasun May 20, 2026
b2a778f
[docs] manifest-diff: reflect that ref derivation lives in the reusab…
amd-hsivasun May 20, 2026
2695bbc
Updated manifest.md
amd-hsivasun May 20, 2026
17c516f
Merge branch 'main' into amd/hsivasun/bump-pr-blamelist
amd-hsivasun May 20, 2026
b8dac59
[CI] manifest-diff: small cleanup pass
amd-hsivasun May 20, 2026
77e99a1
[CI] manifest-diff: drop actions:read perm
amd-hsivasun May 20, 2026
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
68 changes: 54 additions & 14 deletions .github/workflows/manifest-diff.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
workflow_dispatch:
inputs:
start_ref:
description: 'Start commit SHA or workflow run ID (required when using workflow mode or commit SHA mode without find-last-successful)'
description: 'Start commit SHA or workflow run ID (required when using workflow mode or commit SHA mode without find-last-run / pr-base-ref)'
required: false
type: string
end_ref:
Expand All @@ -16,10 +16,24 @@ on:
required: false
type: boolean
default: false
find_last_successful:
description: 'Workflow file to find last successful run (e.g., ci_nightly.yml)'
find_last_run:
description: 'Workflow file to find last run on the branch (e.g., ci_nightly.yml). Run status filter is set via accepted_statuses.'
required: false
type: string
accepted_statuses:
description: 'Comma-separated workflow run statuses accepted by --find-last-run (default: success).'
required: false
type: string
default: 'success'
Comment thread
amd-hsivasun marked this conversation as resolved.
Outdated
pr_base_ref:
description: 'PR base branch name. When set, start_ref is resolved as the merge-base between end_ref and origin/<pr_base_ref> via the GitHub Compare API.'
required: false
type: string
branch:
description: 'Branch to scope --find-last-run lookups against (default: main).'
required: false
type: string
default: 'main'
workflow_call:
inputs:
start_ref:
Expand All @@ -32,46 +46,72 @@ on:
required: false
type: boolean
default: false
find_last_successful:
find_last_run:
required: false
type: string
accepted_statuses:
required: false
type: string
default: 'success'
pr_base_ref:
required: false
type: string
branch:
required: false
type: string
default: 'main'

permissions:
contents: read

jobs:
generate-report:
name: Generate Manifest Diff Report
runs-on: ubuntu-24.04
runs-on: azure-linux-scale-rocm
# Informational job: never fail the parent caller.
continue-on-error: true
permissions:
id-token: write
contents: read
container:
image: ghcr.io/rocm/therock_build_manylinux_x86_64@sha256:702a5133851e6d1daf1207d2c9fbb01c2667914a5b6dc5a01faeb3ce66ea6421
Comment thread
amd-hsivasun marked this conversation as resolved.
Outdated
options: -v /runner/config:/home/awsconfig/
env:
AWS_SHARED_CREDENTIALS_FILE: /home/awsconfig/credentials.ini
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
Comment thread
amd-hsivasun marked this conversation as resolved.
with:
fetch-depth: 0

- name: Adjust git config
run: |
git config --global --add safe.directory $PWD
git config fetch.parallel 10
Comment thread
amd-hsivasun marked this conversation as resolved.
Outdated

- name: Install python deps
run: pip install -r requirements.txt
Comment thread
amd-hsivasun marked this conversation as resolved.
Outdated

- name: Generate manifest diff report
env:
GITHUB_TOKEN: ${{ github.token }}
run: |
python3 build_tools/generate_manifest_diff_report.py \
--end "${{ inputs.end_ref }}" \
--start "${{ inputs.start_ref || '' }}" \
--find-last-successful "${{ inputs.find_last_successful || '' }}" \
--start "${{ inputs.start_ref }}" \
--find-last-run "${{ inputs.find_last_run }}" \
--accepted-statuses "${{ inputs.accepted_statuses }}" \
--pr-base-ref "${{ inputs.pr_base_ref }}" \
--branch "${{ inputs.branch }}" \
${{ inputs.workflow_mode && '--workflow-mode' || '' }} \
--output-dir reports

- name: Configure AWS Credentials
if: ${{ github.repository == 'ROCm/TheRock' }}
uses: aws-actions/configure-aws-credentials@d979d5b3a71173a29b74b5b88418bfda9437d885 # v6.1.1
with:
aws-region: us-east-2
role-to-assume: arn:aws:iam::692859939525:role/therock-ci
- name: Configure AWS credentials for artifact uploads
if: ${{ always() }}
uses: ./.github/actions/configure_aws_artifacts_credentials

- name: Upload Report to S3
if: ${{ github.repository == 'ROCm/TheRock' }}
if: ${{ always() }}
run: |
python3 build_tools/github_actions/upload_test_report_script.py \
--run-id ${{ github.run_id }} \
Expand Down
16 changes: 16 additions & 0 deletions .github/workflows/multi_arch_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,22 @@ jobs:
contents: read
id-token: write

manifest_diff:
name: Manifest Diff
if: github.repository == 'ROCm/TheRock'
Comment thread
amd-hsivasun marked this conversation as resolved.
Outdated
uses: ./.github/workflows/manifest-diff.yml
permissions:
contents: read
id-token: write
with:
end_ref: >-
${{ github.event_name == 'pull_request' && github.event.pull_request.head.sha || github.sha }}
start_ref: >-
${{ github.event_name == 'push' && github.event.before || '' }}
pr_base_ref: >-
${{ github.event_name == 'pull_request' && github.event.pull_request.base.ref || '' }}
branch: ${{ github.event.pull_request.base.ref || github.ref_name }}
Comment thread
amd-hsivasun marked this conversation as resolved.
Outdated

ci_summary:
name: CI Summary
if: always()
Expand Down
134 changes: 99 additions & 35 deletions build_tools/generate_manifest_diff_report.py
Comment thread
amd-hsivasun marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,32 @@
for each component between builds.

Arguments:
--start Start commit SHA or workflow run ID (required unless using --find-last-successful)
--end End commit SHA or workflow run ID (required)
--find-last-successful Workflow file to find last successful run (e.g., 'ci_nightly.yml')
--workflow-mode Treat --start and --end as workflow run IDs instead of commit SHAs
--start Start commit SHA or workflow run ID (required unless using
--find-last-run or --pr-base-ref).
--end End commit SHA or workflow run ID (required).
--find-last-run Workflow file to find the most recent prior run on the
branch whose status is in --accepted-statuses
(e.g., 'ci_nightly.yml').
--accepted-statuses Comma-separated workflow run conclusions accepted by
--find-last-run (default: 'success'). Other examples:
'success,failure'.
--pr-base-ref PR base branch name. When set, --start is resolved as
the merge-base between --end and the named branch via
the GitHub Compare API. Rebase-safe.
--workflow-mode Treat --start and --end as workflow run IDs instead of
commit SHAs.
--branch Branch to scope --find-last-run lookups against
(default: 'main').
--output-dir Directory to write the HTML report into (default: the
TheRock root directory).

If no usable start ref can be derived, the script logs the reason and
exits 0 without writing a report.

Example usage:
python build_tools/generate_manifest_diff_report.py --start abc123 --end def456
python build_tools/generate_manifest_diff_report.py --end def456 --find-last-successful ci_nightly.yml
python build_tools/generate_manifest_diff_report.py --end def456 --find-last-run ci_nightly.yml
python build_tools/generate_manifest_diff_report.py --end def456 --pr-base-ref main
python build_tools/generate_manifest_diff_report.py --start 12345 --end 67890 --workflow-mode
"""

Expand All @@ -34,7 +52,7 @@
from generate_therock_manifest import build_manifest_schema
from github_actions.github_actions_api import (
gha_append_step_summary,
gha_query_last_successful_workflow_run,
gha_query_last_workflow_run,
gha_query_workflow_run_by_id,
gha_send_request,
)
Expand Down Expand Up @@ -181,8 +199,28 @@ def parse_args(argv: list[str] | None) -> argparse.Namespace:
)
parser.add_argument("--end", required=True, help="End workflow ID or commit SHA")
parser.add_argument(
"--find-last-successful",
help="Workflow name to find last successful run (e.g., 'ci_nightly.yml')",
"--find-last-run",
help=(
"Workflow name to find the most recent prior run on the branch "
"whose status is in --accepted-statuses (e.g., 'ci_nightly.yml')."
),
)
parser.add_argument(
"--accepted-statuses",
default="success",
help=(
"Comma-separated workflow run conclusions accepted by "
"--find-last-run (default: 'success'). Examples: 'success', "
"'success,failure'."
),
)
parser.add_argument(
"--pr-base-ref",
help=(
"PR base branch name. When set, --start is resolved as the "
"merge-base between --end and this branch via the GitHub Compare "
"API. Rebase-safe."
),
)
parser.add_argument(
"--workflow-mode",
Expand All @@ -192,7 +230,7 @@ def parse_args(argv: list[str] | None) -> argparse.Namespace:
parser.add_argument(
"--branch",
default="main",
help="Branch to search for last successful workflow run (default: main)",
help="Branch to search for last workflow run (default: main)",
)
parser.add_argument(
"--output-dir",
Expand All @@ -214,42 +252,68 @@ def _optional_str(val: str | None) -> str | None:
return s if s else None


def resolve_commits(args: argparse.Namespace) -> tuple[str, str]:
"""Resolve start and end commit SHAs from arguments."""
def resolve_commits(
args: argparse.Namespace,
) -> tuple[str | None, str | None]:
"""Resolve start and end commit SHAs from arguments.

Returns ``(None, None)`` when --find-last-run finds no prior matching
run (graceful empty); caller should exit 0. Other API errors (e.g.
Compare API 404 on --pr-base-ref) propagate as GitHubAPIError; the
workflow's continue-on-error gate handles those.
"""
start = _optional_str(args.start)
find_last = _optional_str(args.find_last_successful)
find_last = _optional_str(args.find_last_run)
pr_base = _optional_str(args.pr_base_ref)
end = _optional_str(args.end)

if start is None and find_last is None:
raise ValueError(
"--start is required unless --find-last-successful is provided"
)
if end is None:
raise ValueError("--end is required")
if start is None and find_last is None and pr_base is None:
raise ValueError(
"--start is required unless --find-last-run or --pr-base-ref is provided"
)

therock = f"{ROCM_ORG}/{THEROCK_REPO}"

therock_repo_full = f"{ROCM_ORG}/{THEROCK_REPO}"
# Resolve end first; --pr-base-ref needs end_sha to compute the merge-base.
if args.workflow_mode:
end_sha = gha_query_workflow_run_by_id(therock, end).get("head_sha")
else:
end_sha = end

# Resolve start commit
if find_last is not None:
last_run = gha_query_last_successful_workflow_run(
therock_repo_full, find_last, branch=args.branch
# Resolve start, in priority order: --pr-base-ref, --find-last-run,
# --workflow-mode, then plain --start.
if pr_base is not None:
compare_url = (
f"https://api.github.com/repos/{therock}/compare/{pr_base}...{end_sha}"
)
compare = gha_send_request(compare_url)
start_sha = compare.get("merge_base_commit", {}).get("sha")
elif find_last is not None:
accepted = {
tok.strip().lower()
for tok in (args.accepted_statuses or "").split(",")
if tok.strip()
} or {"success"}
last_run = gha_query_last_workflow_run(
therock,
find_last,
branch=args.branch,
accepted_statuses=accepted,
)
if not last_run:
raise ValueError(f"No previous successful run found for {find_last}")
if last_run is None:
print(
f" [empty] No prior run of {find_last} on {args.branch} with "
f"conclusion in {sorted(accepted)} (likely a first-ever run)."
)
return None, None
start_sha = last_run["head_sha"]
elif args.workflow_mode:
workflow_info = gha_query_workflow_run_by_id(therock_repo_full, start)
start_sha = workflow_info.get("head_sha")
start_sha = gha_query_workflow_run_by_id(therock, start).get("head_sha")
else:
start_sha = start

# Resolve end commit
if args.workflow_mode:
workflow_info = gha_query_workflow_run_by_id(therock_repo_full, end)
end_sha = workflow_info.get("head_sha")
else:
end_sha = end

return start_sha, end_sha


Expand Down Expand Up @@ -1423,11 +1487,11 @@ def main(argv: list[str] | None = None) -> int:
"""Main entry point."""
args = parse_args(argv)
start_commit, end_commit = resolve_commits(args)
if start_commit is None:
return 0

diff = compare_manifests(start_commit, end_commit)

output_dir = args.output_dir
generate_html_report(diff, output_dir)
generate_html_report(diff, args.output_dir)

print("\n=== Generating Step Summary ===")
generate_step_summary(diff)
Expand Down
1 change: 1 addition & 0 deletions build_tools/github_actions/configure_ci_path_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ def is_ci_run_required(paths: Optional[Iterable[str]]) -> bool:
"multi_arch_build_windows_artifacts.yml",
"multi_arch_build_wsl_rocdxg_artifacts.yml",
"setup_multi_arch.yml",
"manifest-diff.yml",
"test_artifacts_structure.yml",
"test_native_linux_packages_install.yml",
# both
Expand Down
33 changes: 18 additions & 15 deletions build_tools/github_actions/github_actions_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,29 +474,32 @@ def gha_query_workflow_runs_for_commit(
return runs


def gha_query_last_successful_workflow_run(
def gha_query_last_workflow_run(
github_repository: str = "ROCm/TheRock",
workflow_name: str = "multi_arch_ci.yml",
branch: str = "main",
accepted_statuses: set[str] | None = None,
) -> dict | None:
"""Find the last successful run of a specific workflow on the specified branch.
"""Find the most recent run of a workflow on ``branch`` whose conclusion
is in ``accepted_statuses`` (default ``{"success"}``).

Args:
github_repository: Repository in format "owner/repo"
workflow_name: Name of the workflow file (e.g., "ci_nightly.yml")
branch: Branch to filter by (defaults to "main")
Filters client-side from the most-recent 100 runs because the
workflow-runs endpoint accepts at most one ``status=`` filter.

Returns:
The full workflow run object of the most recent successful run on the specified branch,
or None if no successful runs are found.
Returns the matching run dict, or ``None`` if none of the last ~100
runs on ``branch`` has an accepted conclusion.
"""
# Use GitHub API query parameters to pre-filter for successful runs on the specified branch
url = f"https://api.github.com/repos/{github_repository}/actions/workflows/{workflow_name}/runs?status=success&branch={branch}&per_page=100&sort=created&direction=desc"
if accepted_statuses is None:
accepted_statuses = {"success"}
url = (
f"https://api.github.com/repos/{github_repository}"
f"/actions/workflows/{workflow_name}/runs"
f"?branch={branch}&per_page=100&sort=created&direction=desc"
)
response = gha_send_request(url)

# Return the first (most recent) successful run
if response and response.get("workflow_runs"):
return response["workflow_runs"][0]
for run in response.get("workflow_runs", []) if response else []:
if run.get("conclusion") in accepted_statuses:
return run
return None


Expand Down
Loading
Loading