Skip to content
Open
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
176 changes: 174 additions & 2 deletions update_deps/helper_scripts/git_operations.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
24 changes: 22 additions & 2 deletions update_deps/helper_scripts/pr_watch_utils.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -339,14 +339,34 @@ 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

$display = get-status-display -status $check.Status
$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
}
Expand Down Expand Up @@ -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
{
Expand Down
57 changes: 57 additions & 0 deletions update_deps/helper_scripts/watch_github_pr.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
14 changes: 14 additions & 0 deletions update_deps/propagate_updates.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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++)
{
Expand All @@ -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
Expand Down