diff --git a/.github/workflows/membrowse-report.yml b/.github/workflows/membrowse-report.yml index 79c88db8413..d7877e60580 100644 --- a/.github/workflows/membrowse-report.yml +++ b/.github/workflows/membrowse-report.yml @@ -42,6 +42,8 @@ jobs: outputs: matrix: ${{ steps.set-matrix.outputs.matrix }} target_count: ${{ steps.set-matrix.outputs.target_count }} + skipped_matrix: ${{ steps.set-matrix.outputs.skipped_matrix }} + skipped_count: ${{ steps.set-matrix.outputs.skipped_count }} steps: - name: Checkout repository uses: actions/checkout@v5 @@ -177,3 +179,38 @@ jobs: verbose: INFO # Uncomment to allow CI to pass even when memory budgets are exceeded # dont_fail_on_alerts: true + + # Targets not affected by this push are not rebuilt, but their binaries are + # unchanged. Report them as identical (metadata-only, no build) so every + # target keeps an unbroken per-commit chain instead of a hole on each commit + # that only touched other BSPs. Only runs on push to master; PRs are transient + # and keep the build-affected-only behavior above. + mark-identical: + needs: load-targets + if: ${{ github.event_name == 'push' && needs.load-targets.outputs.skipped_count != '0' }} + runs-on: ubuntu-22.04 + strategy: + fail-fast: false + matrix: + include: ${{ fromJson(needs.load-targets.outputs.skipped_matrix) }} + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Mark MemBrowse report identical + # Non-fatal: an identical upload needs the parent commit to already have + # a report for this target. Right after adoption some parents predate + # this job and the upload is rejected; it self-heals once the target is + # next built (a full report needs no parent) and every later commit is + # covered. Don't fail the run over that transient window. + continue-on-error: true + uses: membrowse/membrowse-action@v1 + with: + target_name: ${{ matrix.target_name }} + identical: true + api_key: ${{ secrets.MEMBROWSE_API_KEY }} + api_url: ${{ vars.MEMBROWSE_API_URL }} + verbose: INFO diff --git a/tools/ci/membrowse_filter_targets.py b/tools/ci/membrowse_filter_targets.py index 626adade8e3..67afa71f78a 100644 --- a/tools/ci/membrowse_filter_targets.py +++ b/tools/ci/membrowse_filter_targets.py @@ -79,13 +79,20 @@ def select_targets(targets, changed_files): return selected -def write_github_output(matrix, output_path): +def skipped_targets(targets, selected_targets): + selected_names = {target["target_name"] for target in selected_targets} + return [target for target in targets if target["target_name"] not in selected_names] + + +def write_github_output(selected, skipped, output_path): if not output_path: return with open(output_path, "a", encoding="utf-8") as output: - output.write("matrix={}\n".format(json.dumps(matrix, separators=(",", ":")))) - output.write("target_count={}\n".format(len(matrix))) + output.write("matrix={}\n".format(json.dumps(selected, separators=(",", ":")))) + output.write("target_count={}\n".format(len(selected))) + output.write("skipped_matrix={}\n".format(json.dumps(skipped, separators=(",", ":")))) + output.write("skipped_count={}\n".format(len(skipped))) def main(): @@ -99,6 +106,10 @@ def main(): changed_files = read_changed_files(args.changed_files) selected_targets = select_targets(targets, changed_files) + # Targets not affected by this change are built by nobody, but their + # binaries are unchanged, so they are reported as identical (metadata-only) + # to keep every target's per-commit chain unbroken. See membrowse-report.yml. + unselected_targets = skipped_targets(targets, selected_targets) print("Changed files:") for changed_file in changed_files: @@ -108,8 +119,12 @@ def main(): for target in selected_targets: print(" {}".format(target["target_name"])) + print("Skipped MemBrowse targets (reported as identical):") + for target in unselected_targets: + print(" {}".format(target["target_name"])) + print("Selected {}/{} MemBrowse targets".format(len(selected_targets), len(targets))) - write_github_output(selected_targets, os.getenv("GITHUB_OUTPUT")) + write_github_output(selected_targets, unselected_targets, os.getenv("GITHUB_OUTPUT")) if __name__ == "__main__":