Skip to content
Closed
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
4 changes: 3 additions & 1 deletion .github/workflows/multi_arch_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,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' }}
# Pull requests reuse the stages their diff does not affect. Pushes stay
# on dry-run so every main commit builds the stages a PR later reuses.
stage_reuse_mode: ${{ inputs.stage_reuse_mode || (github.event_name == 'pull_request' && 'reuse-stage') || '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_python_packages: ${{ github.event_name != 'workflow_dispatch' || inputs.build_python_packages }}
Expand Down
6 changes: 4 additions & 2 deletions .github/workflows/setup_multi_arch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -245,8 +245,10 @@ jobs:
# Which repo to query for baseline_run_id. Defaults to THEROCK_REPOSITORY.
BASELINE_REPOSITORY: ${{ inputs.baseline_repository }}
STAGE_REUSE_MODE: ${{ inputs.stage_reuse_mode }}
# The checkout commit is used for commit-compatibility checking.
STAGE_REUSE_CURRENT_SHA: ${{ steps.checkout.outputs.commit }}
# The checkout commit is used for commit-compatibility checking. A
# pull request checks out a merge commit that is not on the baseline
# branch, so ancestry is established from its base commit instead.
STAGE_REUSE_CURRENT_SHA: ${{ github.event_name == 'pull_request' && github.event.pull_request.base.sha || steps.checkout.outputs.commit }}
STAGE_REUSE_MAX_AGE_HOURS: ${{ inputs.stage_reuse_max_age_hours }}
STAGE_REUSE_COMMIT_HISTORY: ${{ inputs.stage_reuse_commit_history }}
# For baseline run selection, use repository input (defaults to GITHUB_REPOSITORY).
Expand Down
68 changes: 68 additions & 0 deletions build_tools/github_actions/tests/stage_reuse_pr_defaults_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Copyright Advanced Micro Devices, Inc.
# SPDX-License-Identifier: MIT

"""Tests the stage-reuse wiring that makes pull request CI skip build stages.

Both settings under test are workflow expressions with no coverage anywhere
else: nothing at parse or actionlint time notices if they are edited back to
the defaults, and the only symptom would be pull requests quietly rebuilding
stages they do not affect again.

* `multi_arch_ci.yml` selects `reuse-stage` for pull requests. Pushes stay on
`dry-run`, because a push that reused stages would not produce the artifacts
a later pull request reuses.
* `setup_multi_arch.yml` establishes commit ancestry from the pull request base
commit. A pull request checks out a merge commit that is not on the baseline
branch, so `validate_commit_compatibility` cannot place it in the branch
history and rejects every candidate baseline as `unknown`.
"""

import unittest

from workflow_utils import WORKFLOWS_DIR, get_workflow_job, load_workflow

CI_WORKFLOW = "multi_arch_ci.yml"
SETUP_WORKFLOW = "setup_multi_arch.yml"


class StageReuseModeTest(unittest.TestCase):
"""Verifies the stage_reuse_mode passed from multi_arch_ci.yml to setup."""

def setUp(self):
workflow = load_workflow(WORKFLOWS_DIR / CI_WORKFLOW)
self.expression = get_workflow_job(workflow, "setup")["with"][
"stage_reuse_mode"
]

def test_pull_requests_select_reuse_stage(self):
self.assertIn("pull_request", self.expression)
self.assertIn("reuse-stage", self.expression)

def test_pushes_keep_building_every_stage(self):
self.assertIn("dry-run", self.expression)

def test_workflow_dispatch_input_still_wins(self):
self.assertTrue(
self.expression.strip().startswith("${{ inputs.stage_reuse_mode ||"),
f"dispatch input no longer overrides the default: {self.expression!r}",
)


class StageReuseCurrentShaTest(unittest.TestCase):
"""Verifies ancestry uses a commit that is on the baseline branch."""

def setUp(self):
workflow = load_workflow(WORKFLOWS_DIR / SETUP_WORKFLOW)
steps = get_workflow_job(workflow, "setup")["steps"]
configure = next(step for step in steps if step.get("id") == "configure")
self.expression = configure["env"]["STAGE_REUSE_CURRENT_SHA"]

def test_pull_requests_use_the_base_commit(self):
self.assertIn("github.event.pull_request.base.sha", self.expression)

def test_other_events_use_the_checkout_commit(self):
self.assertIn("steps.checkout.outputs.commit", self.expression)


if __name__ == "__main__":
unittest.main()
23 changes: 15 additions & 8 deletions docs/development/stage_reuse.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,19 @@ selected.

Automatic reuse is **disabled** when no build platform is selected.

In Multi-Arch CI, pull requests run in `reuse-stage` and every other trigger
runs in `dry-run`. Pushes deliberately keep building every stage: a push that
reused stages would not produce the artifacts a later pull request reuses.

### Inputs

| Input | Default | Purpose |
| ---------------------------- | --------- | ------------------------------------------------------------------------------------------------------ |
| `stage_reuse_mode` | `dry-run` | `dry-run` (report only) or `reuse-stage` (auto-reuse unaffected stages). |
| `stage_reuse_max_age_hours` | `72` | Reject baseline runs older than this many hours (recency window). |
| `stage_reuse_commit_history` | `50` | Number of recent branch commits to fetch when establishing ancestry for the commit-compatibility rule. |
| `prebuilt_stages` | `""` | Manual, comma-separated stages to reuse (or `all`). Always honored, independent of `stage_reuse_mode`. |
| `baseline_run_id` | `""` | Run ID to copy manually-listed `prebuilt_stages` artifacts from. |
| Input | Default | Purpose |
| ---------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `stage_reuse_mode` | `dry-run` | `dry-run` (report only) or `reuse-stage` (auto-reuse unaffected stages). Multi-Arch CI passes `reuse-stage` for pull requests. |
| `stage_reuse_max_age_hours` | `72` | Reject baseline runs older than this many hours (recency window). |
| `stage_reuse_commit_history` | `50` | Number of recent branch commits to fetch when establishing ancestry for the commit-compatibility rule. |
| `prebuilt_stages` | `""` | Manual, comma-separated stages to reuse (or `all`). Always honored, independent of `stage_reuse_mode`. |
| `baseline_run_id` | `""` | Run ID to copy manually-listed `prebuilt_stages` artifacts from. |

Example `workflow_call` from a component CI:

Expand All @@ -86,7 +90,10 @@ A baseline run is only used when it is:
(default `multi_arch_ci.yml`),
- recent enough (`stage_reuse_max_age_hours`),
- commit-compatible — its commit is the same as, or an ancestor of, the current
commit (established from the last `stage_reuse_commit_history` commits), and
commit (established from the last `stage_reuse_commit_history` commits). On a
pull request the "current commit" is the base commit rather than the checked
out merge commit, which is not on the baseline branch and so cannot be placed
in that history; and
- healthy — its `Build` jobs succeeded and it contains every required artifact
for every platform being built.

Expand Down
Loading