diff --git a/update_deps/helper_scripts/git_operations.ps1 b/update_deps/helper_scripts/git_operations.ps1 index a9ad0e5..eb939a0 100644 --- a/update_deps/helper_scripts/git_operations.ps1 +++ b/update_deps/helper_scripts/git_operations.ps1 @@ -23,6 +23,178 @@ function create-ignore-pattern # Initialize the ignore pattern when script is sourced create-ignore-pattern +# Snapshot the current master HEAD commit for all repos in the work directory. +# Called once before propagation starts so that every repo is updated to the +# same set of commits throughout the entire run. +function snapshot-repo-commits +{ + param( + [string[]] $repo_order + ) + $commits = @{} + # Calculate max repo name length for aligned output + $max_name_len = ($repo_order | ForEach-Object { $_.Length } | Measure-Object -Maximum).Maximum + foreach ($repo_name in $repo_order) + { + if (Test-Path $repo_name) + { + Push-Location $repo_name + $sha = (git rev-parse master 2>$null) + if ($LASTEXITCODE -eq 0 -and $sha) + { + $commits[$repo_name] = $sha.Trim() + Write-Host (" {0,-$max_name_len} : {1}" -f $repo_name, $commits[$repo_name].Substring(0, 8)) + } + else + { + Write-Host " Warning: Could not get master commit for $repo_name" -ForegroundColor Yellow + } + Pop-Location + } + else + { + # repo not cloned yet, skip + } + } + return $commits +} + +# Update each submodule to its fixed commit, or latest master if no fixed commit is available +function update-submodules-to-fixed-commits +{ + # Parse ignore pattern into a list for matching + $ignore_paths = @() + if ($global:ignore_pattern) + { + $ignore_paths = $global:ignore_pattern -split '\|' + } + + # Get submodule paths from .gitmodules + if (Test-Path ".gitmodules") + { + $submodule_lines = git config --file .gitmodules --get-regexp '\.path$' + if ($submodule_lines) + { + foreach ($line in $submodule_lines) + { + $sub_path = ($line -split "\s+", 2)[1] + + # Check if this submodule should be ignored + if ($sub_path -in $ignore_paths) + { + # ignored submodule, skip + } + else + { + # Derive repo name from submodule path (e.g., "deps/c-util" -> "c-util") + $sub_repo_name = Split-Path $sub_path -Leaf + + Push-Location $sub_path + if ($global:fixed_commits -and $global:fixed_commits.ContainsKey($sub_repo_name)) + { + $target_sha = $global:fixed_commits[$sub_repo_name] + Write-Host " Checking out $sub_path at fixed commit $($target_sha.Substring(0, 8))" + git fetch origin + git checkout $target_sha + + # Warn if remote master has moved ahead of the fixed commit + $remote_sha = (git rev-parse origin/master 2>$null) + if ($LASTEXITCODE -eq 0 -and $remote_sha -and $remote_sha.Trim() -ne $target_sha) + { + Write-Host " WARNING: $sub_repo_name has newer commits on master ($($remote_sha.Trim().Substring(0, 8))) that will NOT be propagated" -ForegroundColor Yellow + if (-not $global:skipped_newer_commits) + { + $global:skipped_newer_commits = @{} + } + $global:skipped_newer_commits[$sub_repo_name] = @{ + FixedCommit = $target_sha + RemoteCommit = $remote_sha.Trim() + } + } + else + { + # remote master matches fixed commit + } + } + else + { + Write-Host " Updating $sub_path to latest master (no fixed commit)" + git checkout master + git pull + } + Pop-Location + } + } + } + else + { + # no submodules found + } + } + else + { + # no .gitmodules file + } +} + +# After a repo's PR is merged, fetch the new master HEAD and update fixed_commits +# so that downstream repos use the commit created by this propagation. +function update-fixed-commit +{ + param( + [string] $repo_name + ) + + if ($global:fixed_commits) + { + Push-Location $repo_name + git fetch origin master 2>$null + $new_sha = (git rev-parse origin/master 2>$null) + if ($LASTEXITCODE -eq 0 -and $new_sha) + { + $global:fixed_commits[$repo_name] = $new_sha.Trim() + Write-Host " Updated fixed commit for $repo_name to $($new_sha.Trim().Substring(0, 8))" + } + else + { + Write-Host " Warning: Could not fetch new master commit for $repo_name" -ForegroundColor Yellow + } + Pop-Location + } + else + { + # no fixed commits table, nothing to update + } +} + +# Show a summary of repos that had newer commits on master that were not propagated +function show-skipped-commits-summary +{ + if ($global:skipped_newer_commits -and $global:skipped_newer_commits.Count -gt 0) + { + Write-Host "" + Write-Host "========================================" -ForegroundColor Yellow + Write-Host " NEWER COMMITS NOT PROPAGATED" -ForegroundColor Yellow + Write-Host "========================================" -ForegroundColor Yellow + Write-Host "The following repos had newer commits on master" -ForegroundColor Yellow + Write-Host "that were not included in this propagation run:" -ForegroundColor Yellow + Write-Host "" + foreach ($repo in $global:skipped_newer_commits.Keys) + { + $info = $global:skipped_newer_commits[$repo] + Write-Host " $repo" -ForegroundColor Yellow -NoNewline + Write-Host " used: $($info.FixedCommit.Substring(0, 8)) remote: $($info.RemoteCommit.Substring(0, 8))" + } + Write-Host "" + Write-Host "Consider running propagation again to pick up these changes." -ForegroundColor Yellow + Write-Host "========================================" -ForegroundColor Yellow + } + else + { + # all repos were up to date + } +} + function refresh-submodules { $submodules = git submodule | Out-String @@ -64,8 +236,8 @@ function update-local-repo # no deps folder } git submodule update --init - # update all submodules except the ones mentioned in ignores.json - git submodule foreach "case `$name in $ignore_pattern ) ;; *) git checkout master && git pull;; esac" + # update all submodules to their fixed commits (or latest master as fallback) + update-submodules-to-fixed-commits # create new branch git checkout -B $new_branch_name # add updates and push to remote diff --git a/update_deps/helper_scripts/pr_watch_utils.ps1 b/update_deps/helper_scripts/pr_watch_utils.ps1 index c2b61ea..cfa3807 100644 --- a/update_deps/helper_scripts/pr_watch_utils.ps1 +++ b/update_deps/helper_scripts/pr_watch_utils.ps1 @@ -339,7 +339,17 @@ function global:show-pr-check-table # Display each check foreach($check in $checks) { - $check_name = truncate-string -text $check.Name -max_width $name_width + $check_name = $check.Name + # Mark optional (non-blocking) checks + if($check.IsBlocking -eq $false) + { + $check_name = $check_name + " (optional)" + } + else + { + # required or unknown blocking status + } + $check_name = truncate-string -text $check_name -max_width $name_width $elapsed = format-elapsed-time -start_time $check.StartTime -finish_time $check.FinishTime $url = truncate-string -text $check.Url -max_width $url_width @@ -347,6 +357,16 @@ function global:show-pr-check-table $symbol = $display.Symbol $color = $display.Color + # Use dimmer color for optional failed checks + if($check.IsBlocking -eq $false -and $check.Status -eq [PrCheckStatus]::Failed) + { + $color = "DarkYellow" + } + else + { + # use default color + } + $line = "{0} {1,-$name_width} {2,-$elapsed_width} {3}" -f $symbol, $check_name, $elapsed, $url Write-Host $line -ForegroundColor $color } @@ -391,7 +411,7 @@ function global:get-check-status-counts # Test if checks are complete # # For Azure DevOps: checks have IsBlocking property, only blocking checks matter -# For GitHub: all checks matter (IsBlocking = $null means treat as blocking) +# For GitHub: uses --required flag to identify required checks (IsBlocking = $false for optional) # function global:Test-ChecksComplete { diff --git a/update_deps/helper_scripts/watch_github_pr.ps1 b/update_deps/helper_scripts/watch_github_pr.ps1 index b4caa1a..928bf62 100644 --- a/update_deps/helper_scripts/watch_github_pr.ps1 +++ b/update_deps/helper_scripts/watch_github_pr.ps1 @@ -176,18 +176,75 @@ function watch-github-pr-checks } else { + # Get list of required check names + $required_names = @{} + $required_output = gh pr checks --required --json name 2>&1 + if($LASTEXITCODE -eq 0 -and $required_output) + { + $required_checks = $required_output | ConvertFrom-Json + if($required_checks) + { + foreach($rc in $required_checks) + { + $required_names[$rc.name] = $true + } + } + else + { + # no required checks parsed + } + } + else + { + # couldn't get required checks, treat all as blocking + } + # Build normalized check items $normalized_checks = @() foreach($check in $checks) { $normalized_status = convert-github-bucket-to-normalized -bucket $check.bucket + # If we got required check info, use it; otherwise leave IsBlocking as $null (all blocking) + $is_blocking = $null + if($required_names.Count -gt 0) + { + # Check exact match first, then check if this is a child job + # of a required pipeline (e.g., "Gate (Build x64)" is a child of "Gate") + if($required_names.ContainsKey($check.name)) + { + $is_blocking = $true + } + else + { + $is_child = $false + foreach($req_name in $required_names.Keys) + { + if($check.name.StartsWith("$req_name ")) + { + $is_child = $true + break + } + else + { + # not a child of this required check + } + } + $is_blocking = $is_child + } + } + else + { + # no required info available, default to blocking + } + $normalized_checks += [PSCustomObject]@{ Name = $check.name Status = $normalized_status StartTime = $check.startedAt FinishTime = $check.completedAt Url = $check.link + IsBlocking = $is_blocking } } diff --git a/update_deps/propagate_updates.ps1 b/update_deps/propagate_updates.ps1 index 8047348..5d1885b 100644 --- a/update_deps/propagate_updates.ps1 +++ b/update_deps/propagate_updates.ps1 @@ -113,11 +113,13 @@ function update-repo { $pr_url = update-repo-github $repo_name $new_branch_name set-repo-status -repo_name $repo_name -status $script:STATUS_UPDATED -pr_url $pr_url + update-fixed-commit $repo_name } elseif ($repo_type -eq "azure") { $pr_url = update-repo-azure $repo_name $new_branch_name set-repo-status -repo_name $repo_name -status $script:STATUS_UPDATED -pr_url $pr_url + update-fixed-commit $repo_name } else { @@ -231,6 +233,14 @@ function propagate-updates # Initialize status tracking initialize-repo-status -repos $repo_order + # Snapshot master HEAD commits for all repos before starting updates. + # This ensures we propagate the same commit for each dependency throughout + # the entire run, even if a repo's master branch is updated externally. + Write-Host "`nSnapshotting master commits for all repos..." + Set-Location $global:work_dir + $global:fixed_commits = snapshot-repo-commits -repo_order $repo_order + Write-Host "Fixed commits captured for $($global:fixed_commits.Count) repos`n" + Write-Host "Updating repositories in the following order: " for($i = 0; $i -lt $repo_order.Length; $i++) { @@ -244,6 +254,10 @@ function propagate-updates # Show final status and check if all succeeded $success = show-propagation-status -Final + + # Warn about any repos with newer commits that were not propagated + show-skipped-commits-summary + if ($success) { play-success-animation