Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1928e80
Adding reuse based on artifact availability and unaffected stages
Jun 30, 2026
1097ec2
Adding reuse based on artifact availability and unaffected stages
Jun 30, 2026
b0d8f67
Adding test cases and fixing bugs
Jun 30, 2026
7bd631c
Moving tests to right location
Jun 30, 2026
58b0a1d
removing redundant import
Jun 30, 2026
9b7a0e1
Fixing copilot detected bug
Jun 30, 2026
fc21126
Fixing formatting
Jun 30, 2026
0648dcc
Adding unit tests
Jun 30, 2026
78fef0c
Merge branch 'main' into users/ammallya/reuse-artifacts
erman-gurses Jul 6, 2026
886eafc
Format
erman-gurses Jul 6, 2026
ce75e62
ci(stage-reuse): verify baseline artifacts per-platform before reusin…
Jul 6, 2026
76397f3
Adding missing debug outputs
Jul 6, 2026
c6fb127
Removing reduandant import
Jul 6, 2026
fe28af9
stage-reuse: disable automatic reuse when no platforms are selected
erman-gurses Jul 7, 2026
4259e85
stage-reuse: simplify build platform selection
erman-gurses Jul 7, 2026
da6adb4
Update docstring
erman-gurses Jul 7, 2026
6851c06
stage-reuse: propagate baseline run id for auto reuse
erman-gurses Jul 7, 2026
397e824
stage-reuse: disable reuse when platforms select different baselines
erman-gurses Jul 7, 2026
e7086a2
Black
erman-gurses Jul 7, 2026
d7c3c09
Merge branch 'main' into users/ammallya/reuse-artifacts
erman-gurses Jul 7, 2026
cb85bc7
Adding documentation
Jul 8, 2026
07f7f7f
fixing ordering regression
Jul 8, 2026
b1f3c37
Refactor and clean up based on Scott's review
Jul 16, 2026
0673a91
Merge branch 'main' into users/ammallya/reuse-artifacts
ammallya Jul 16, 2026
5dc07b4
Fix after new JAX functionality merge
Jul 16, 2026
1c19f0b
Fix for tests
Jul 17, 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
19 changes: 19 additions & 0 deletions .github/workflows/multi_arch_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,22 @@ on:
type: string
default: ""
description: "Workflow run ID to copy prebuilt stage artifacts from; required when prebuilt_stages is set"
stage_reuse_mode:
type: string
default: "dry-run"
# This drives the AUTOMATIC stage-reuse detection layer, which is
# separate from the manual `prebuilt_stages` input above. dry-run only
# reports which unaffected stages could be reused; reuse-stage actually
# reuses them (skips the build and copies artifacts from a baseline run).
description: "dry-run (report only) | reuse-stage (auto-reuse unaffected stages)"
Comment thread
ammallya marked this conversation as resolved.
Resolved
stage_reuse_max_age_hours:
type: string
default: "72"
description: "Automatic stage reuse: reject baseline runs older than this many hours"
stage_reuse_commit_history:
type: string
default: "50"
description: "Automatic stage reuse: number of recent branch commits to fetch for ancestry"
changed_projects:
type: string
default: ""
Expand Down Expand Up @@ -82,6 +98,9 @@ jobs:
windows_test_labels: ${{ inputs.windows_test_labels || '' }}
prebuilt_stages: ${{ inputs.prebuilt_stages || '' }}
baseline_run_id: ${{ inputs.baseline_run_id || '' }}
stage_reuse_mode: ${{ inputs.stage_reuse_mode || 'dry-run' }}
stage_reuse_max_age_hours: ${{ inputs.stage_reuse_max_age_hours || '72' }}
stage_reuse_commit_history: ${{ inputs.stage_reuse_commit_history || '50' }}
build_pytorch: ${{ github.event_name != 'workflow_dispatch' || inputs.build_pytorch }}
build_jax: false

Expand Down
21 changes: 21 additions & 0 deletions .github/workflows/setup_multi_arch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,23 @@ on:
description: "Workflow run ID to copy prebuilt stage artifacts from."
type: string
default: ""
stage_reuse_mode:
description: >-
Automatic stage-reuse mode (separate from the manual prebuilt_stages
input): "dry-run" (default) only reports which unaffected stages could
be reused; "reuse-stage" actually reuses them.
type: string
default: "dry-run"
stage_reuse_max_age_hours:
description: >-
Automatic stage reuse: reject baseline runs older than this many hours. default: "72"
type: string
default: "72"
stage_reuse_commit_history:
description: >-
Automatic stage reuse: number of recent branch commits to fetch for ancestry. default: "50"
type: string
default: "50"
build_pytorch:
description: "Build PyTorch wheels."
type: boolean
Expand Down Expand Up @@ -159,6 +176,10 @@ jobs:
WINDOWS_TEST_LABELS: ${{ inputs.windows_test_labels }}
PREBUILT_STAGES: ${{ inputs.prebuilt_stages }}
BASELINE_RUN_ID: ${{ inputs.baseline_run_id }}
STAGE_REUSE_MODE: ${{ inputs.stage_reuse_mode }}
STAGE_REUSE_CURRENT_SHA: ${{ steps.checkout.outputs.commit }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

you can actaully get this from ${{ github.sha }} (current sha)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It depends on what commit ref you want.

steps.checkout.outputs.commit gets the TheRock commit even when running in rockrel (and maybe rocm-libraries / rocm-systems?), see the workflow output:

For runs in rocm-libraries and rocm-systems, we may want to use baseline runs from those repositories instead of TheRock though.

Ultimately I think the best artifact reuse we'll be able to get will be some query against a database where we say "here are the artifact fingerprints that my current source code produces, fetch me artifacts for any with matching fingerprints from the database", which we'd want to abstract away from the github actions runs API entirely. We wouldn't need to scan commit history or workflow runs with that architecture.

STAGE_REUSE_MAX_AGE_HOURS: ${{ inputs.stage_reuse_max_age_hours }}
STAGE_REUSE_COMMIT_HISTORY: ${{ inputs.stage_reuse_commit_history }}
# Skip path filtering for external repos (git sha won't exist in TheRock)
SKIP_PATH_FILTERS: ${{ inputs.external_repo != '' && 'true' || '' }}
CI_CONFIG_PATH: ci-config
Expand Down
80 changes: 75 additions & 5 deletions build_tools/github_actions/configure_multi_arch_ci.py
Original file line number Diff line number Diff line change
Expand Up @@ -681,18 +681,61 @@ def _determine_test_type(
def decide_jobs(
ci_inputs: CIInputs,
git_context: GitContext,
targets: "TargetSelection | None" = None,
) -> JobDecisions:
"""Determine which job groups to run, skip, or satisfy with prebuilt files."""
"""Determine which job groups to run, skip, or satisfy with prebuilt files.
``targets`` (the per-platform family selection) is used to scope automatic
stage reuse to the platforms actually being built, so a stage is only
reused when its artifacts exist for every one of those platforms.
"""

# Build ROCm.
# TODO(#3399): Use changed files and build_topology.py to:
# 1. set per-stage prebuilt decisions
# 2. skip job groups that aren't reachable from the changed files
# Parse explicit prebuilt stages from workflow_dispatch input.
# Parse explicit prebuilt stages from workflow_dispatch input. These are
# the MANUAL inputs and are always honored, unchanged, in every mode.

stage_decisions: dict[str, JobAction] = {}
if ci_inputs.prebuilt_stages:
for stage in _parse_prebuilt_stages(ci_inputs.prebuilt_stages):
stage_decisions[stage] = JobAction.PREBUILT

# Automatic stage reuse, behind STAGE_REUSE_MODE: in dry-run we only report
# which stages WOULD be reused and apply nothing; in "reuse-stage" the
# eligible stages are merged into stage_decisions so the orchestrator skips
# their builds and copies artifacts instead.
# These imports are intentionally local. stage_reuse_decision imports from
# sibling CI modules (baseline_runs, stage_impact, github_actions_api) that
# in turn pull in heavier deps; importing it lazily here keeps module import
# of configure_multi_arch_ci cheap and avoids an import cycle between the two
# modules (stage_reuse plumbing conceptually depends on this pipeline).

from stage_reuse_decision import (
StageReuseMode,
compute_auto_stage_reuse,
render_step_summary,
log_report,
stage_reuse_target_families,
)

build_platforms = _stage_reuse_build_platforms(ci_inputs, targets)
Comment thread
ammallya marked this conversation as resolved.
Resolved
target_families = stage_reuse_target_families(
ci_inputs.linux_amdgpu_families,
ci_inputs.windows_amdgpu_families,
)
auto = compute_auto_stage_reuse(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

compute_auto_stage_reuse() now performs stage-impact analysis, baseline selection, artifact verification, and reporting. Have we considered separating the planning portion from the reporting/execution plumbing to keep the decision logic independently reusable?

Comment thread
ammallya marked this conversation as resolved.
Resolved
changed_files=git_context.changed_files,
mode=StageReuseMode.from_environ(),
platforms=build_platforms,
target_families=target_families,
)
log_report(auto)
Comment thread
ammallya marked this conversation as resolved.
Resolved

try:
gha_append_step_summary(render_step_summary(auto))
Comment thread
ammallya marked this conversation as resolved.
Resolved
except Exception as exc: # never fail CI config on a reporting error
print(f"[STAGE-REUSE] could not write step summary: {exc}")
Comment thread
ammallya marked this conversation as resolved.
Resolved
# Only reuse-stage mode returns non-empty applied_reuse_stages.
for stage in auto.applied_reuse_stages:
Comment thread
erman-gurses marked this conversation as resolved.
Resolved
stage_decisions.setdefault(stage, JobAction.PREBUILT)
build_rocm = BuildRocmDecision(
action=JobAction.RUN,
stage_decisions=stage_decisions,
Expand Down Expand Up @@ -742,6 +785,33 @@ def decide_jobs(
# ---------------------------------------------------------------------------


def _stage_reuse_build_platforms(
ci_inputs: "CIInputs",
targets: "TargetSelection | None",
Comment thread
ammallya marked this conversation as resolved.
Resolved
) -> list[str]:
"""Platforms whose baselines must contain a stage's artifacts to reuse it.
Automatic reuse merges into the prebuilt_stages that flow to BOTH the Linux
and Windows build configs, so a stage may only be reused when its artifacts
are available for every platform actually being built. When target selection
is known we use it directly; otherwise we fall back to the requested family
inputs, defaulting to Linux only.
"""
if targets is not None:
platforms = []
if targets.linux_families:
platforms.append("linux")
if targets.windows_families:
platforms.append("windows")
return platforms or ["linux"]
Comment thread
erman-gurses marked this conversation as resolved.
Resolved

platforms = []
if ci_inputs.linux_amdgpu_families:
platforms.append("linux")
if ci_inputs.windows_amdgpu_families:
platforms.append("windows")
return platforms or ["linux"]
Comment thread
erman-gurses marked this conversation as resolved.
Resolved


def _validate_family_names(
names: list[str],
known: dict[str, dict],
Expand Down
Loading
Loading