Skip to content

Add opt-in Bug Scouter PR analysis for azure-ai-ml - #48503

Open
mohammadsheraj wants to merge 3 commits into
Azure:mainfrom
mohammadsheraj:feature/bug-scouter-upstream
Open

Add opt-in Bug Scouter PR analysis for azure-ai-ml#48503
mohammadsheraj wants to merge 3 commits into
Azure:mainfrom
mohammadsheraj:feature/bug-scouter-upstream

Conversation

@mohammadsheraj

@mohammadsheraj mohammadsheraj commented Aug 8, 2026

Copy link
Copy Markdown

What

Adds an opt-in bug-scouter PR analysis pilot for changes under sdk/ml/azure-ai-ml.

  • builds the exact PR SHA in an unprivileged pull_request workflow
  • runs only when the exact bug-scouter label is present
  • revalidates the open PR, live head SHA, and label before context collection and again immediately before staging
  • builds bug-bash instructions from the PR title, description, issue comments, reviews, inline review comments, exact base-to-head diff, and package README
  • validates live PR provenance and safely downloads/extracts bounded artifacts in a trusted workflow_run
  • uses short-lived Azure OIDC only after validation
  • never installs, imports, or executes the PR wheel in the trusted GitHub job
  • publishes build, context-preparation, OIDC, infrastructure, and analysis outcomes
  • deletes each run-scoped staging blob after completion or failure

Applying the bug-scouter label is an explicit authorization for the hosted Bug Scouter service to install and test that exact PR wheel. Remove the label to block invocation before staging; pushing a new commit invalidates an older run through the live SHA check.

Context: #48502

Security boundary

The build job has read-only contents access and no Azure identity or write token. The trusted job pins action dependencies, checks the upstream repository/workflow/run/live SHA/head repository/label, rejects unexpected paths, symlinks, sizes, digest mismatches, oversized GitHub responses, and excessive pagination, then invokes Foundry. Fork PRs are supported without granting credentials to PR code in GitHub Actions.

PR-controlled wheel execution occurs in the labeled hosted Bug Scouter run. The current service isolation boundary is its hosted container and per-run virtual environment; the label must therefore be applied only to PRs approved for that execution boundary.

Validation

  • python -m pytest .github/scripts/test_prepare_bugscouter.py .github/scripts/test_run_bugscouter.py -q (48 passed)
  • repository changed-files cspell (0 issues)
  • actionlint 1.7.12 for both workflows
  • Black, Python compilation, VS Code diagnostics, and git diff --check
  • hosted Bug Scouter agent version 54 deployed successfully from service commit 7663ce3

Administrator setup after approval

A repository administrator must create the bug-scouter-prod environment and configure the six referenced BUGSCOUTER_* variables. Azure federation and least-privilege RBAC are configured separately; no cloud secrets are stored in GitHub.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
10 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@mohammadsheraj
mohammadsheraj marked this pull request as ready for review August 10, 2026 02:57
@mohammadsheraj
mohammadsheraj requested a review from a team as a code owner August 10, 2026 02:57
Copilot AI balanced review requested due to automatic review settings August 10, 2026 02:57
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).
10 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

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.

Pull request overview

Adds opt-in Bug Scouter analysis for labeled azure-ai-ml pull requests using separated untrusted build and trusted invocation workflows.

Changes:

  • Builds and packages exact-commit PR inputs.
  • Validates provenance, artifacts, authorization, and Azure OIDC invocation.
  • Adds focused preparation, publication, and failure-handling tests.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
.github/workflows/bug-scouter-build.yml Builds and uploads exact-SHA inputs.
.github/workflows/bug-scouter-invoke.yml Validates, invokes, and publishes results.
.github/scripts/prepare_bugscouter.py Validates artifacts and assembles PR context.
.github/scripts/run_bugscouter.py Invokes Bug Scouter and publishes outcomes.
.github/scripts/test_prepare_bugscouter.py Tests provenance and artifact validation.
.github/scripts/test_run_bugscouter.py Tests invocation and result publication.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

build-wheel:
name: Build exact PR wheel
if: contains(github.event.pull_request.labels.*.name, 'bug-scouter')
runs-on: ubuntu-latest
Comment on lines +350 to +352
existing = next(
(comment for comment in comments if marker in str(comment.get("body") or "")),
None,
Comment on lines +15 to +19
invoke:
name: Validate, invoke, and publish
runs-on: ubuntu-latest
timeout-minutes: 100
environment: bug-scouter-prod
Comment on lines +285 to +295
def _bug_count(result: dict) -> int:
value = result.get("real_bug_count")
if isinstance(value, bool):
raise ValueError("real_bug_count must be an integer")
try:
bug_count = int(value)
except (TypeError, ValueError) as exc:
raise ValueError("real_bug_count must be an integer") from exc
if bug_count < 0 or bug_count > MAX_BUG_COUNT:
raise ValueError("real_bug_count is outside the allowed range")
return bug_count
Comment on lines +339 to +350
with zipfile.ZipFile(archive_file) as archive:
members = [member for member in archive.infolist() if not member.is_dir()]
names = {member.filename for member in members}
wheel_names = [name for name in names if WHEEL_PATTERN.fullmatch(name)]
if len(wheel_names) != 1 or names != {
"manifest.json",
"README.md",
wheel_names[0],
}:
raise ValueError(
"artifact archive contains missing, nested, or unexpected files"
)
Copilot AI review requested due to automatic review settings August 10, 2026 03:17

Copilot AI left a comment

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.

Pull request overview

Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.

Suppressed comments (4)

.github/scripts/run_bugscouter.py:350

  • When all ten pages are full, the search silently proceeds as if no marker exists. On a PR that already has at least 1,000 comments, the first Bug Scouter comment lands after the search window and every rerun creates another comment, breaking the promised idempotency. Fail closed when the pagination bound is exhausted, as prepare_bugscouter._github_pages already does.
        if len(batch) < 100:
            break
    existing = next(

.github/scripts/run_bugscouter.py:282

  • The length cap is applied before escaping @, but each replacement expands one character to five. A valid 60,000-character service summary containing many mentions can therefore become hundreds of thousands of characters and exceed GitHub's issue-comment body limit, causing result publication to fail. Escape first and truncate the escaped value.
    summary = str(value or fallback)[:MAX_SUMMARY_CHARACTERS]
    return summary.replace("@", "&#64;")

.github/scripts/run_bugscouter.py:190

  • The live label/SHA authorization is checked before hashing a wheel of up to 200 MiB, leaving a revocation window in which the label can be removed before staging begins but the blob is still uploaded. Compute the hash and blob name first, then perform the live check immediately before _upload_blob so the opt-in check actually guards staging as described.

This issue also appears in the following locations of the same file:

  • line 281
  • line 348
    _validate_live_authorization(values, github_token)

    wheel_hash = hashlib.sha256(wheel_path.read_bytes()).hexdigest()

.github/workflows/bug-scouter-invoke.yml:19

  • The protected environment is entered for every completed Bug Scouter Build run, not only opted-in runs. The build workflow itself starts for every matching PR and only skips its sole job when the label is absent; an all-skipped workflow still completes successfully and triggers this workflow_run, so unlabeled PRs can queue this environment job and then fail in resolve. Split the preflight/label validation into a job without the environment and gate a second environment-scoped invocation job on its validated output (while retaining a non-environment failure-publication path).
    environment: bug-scouter-prod

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants