Skip to content

Update Vendor

Update Vendor #1374

Workflow file for this run

name: Update Vendor
on:
workflow_dispatch:
schedule:
# At 13:37 UTC every day.
- cron: '37 13 * * *'
concurrency:
group: vendor-update
cancel-in-progress: false
env:
# Fork owners can opt in by setting this Actions repository variable to true.
AUTO_MERGE_MINOR_VENDOR_UPDATES: ${{ vars.AUTO_MERGE_MINOR_VENDOR_UPDATES || 'false' }}
defaults:
run:
shell: pwsh
permissions:
contents: read
jobs:
vendor:
runs-on: windows-latest
continue-on-error: false
timeout-minutes: 15
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Configure auto-merge policy
shell: pwsh
run: |
$rawValue = [string]$env:AUTO_MERGE_MINOR_VENDOR_UPDATES
$normalizedValue = $rawValue.Trim().ToLowerInvariant()
$enabled = $normalizedValue -in @('true', '1', 'yes', 'on')
if ($enabled) {
Add-Content -Path $env:GITHUB_ENV -Value "AUTO_MERGE_MINOR_VENDOR_UPDATES_ENABLED=True"
} else {
Add-Content -Path $env:GITHUB_ENV -Value "AUTO_MERGE_MINOR_VENDOR_UPDATES_ENABLED=False"
}
- name: Summary - Workflow started
shell: pwsh
run: |
$summary = @"
## 📦 Update Vendor - Workflow Summary
Checking for vendor dependency updates...
"@
$summary | Add-Content -Path $env:GITHUB_STEP_SUMMARY -Encoding utf8
- id: make-changes
name: Checking for updates
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
$currentVersion = (Get-Content -Raw .\vendor\sources.json | ConvertFrom-Json)
. .\scripts\update.ps1 -verbose
# Export count of updated packages (update.ps1 is expected to set $count)
if (-not ($count)) { $count = 0 }
Set-GHVariable -Name COUNT_UPDATED -Value $count
$newVersion = (Get-Content -Raw .\vendor\sources.json | ConvertFrom-Json)
# Source utility functions
. scripts/utils.ps1
$listUpdated = ""
$updateMessage = "| Name | Old Version | New Version |`n| :--- | :---: | :---: |`n"
$majorUpdates = @()
$singleDepName = ""
$singleDepOldVersion = ""
$singleDepNewVersion = ""
foreach ($s in $newVersion) {
$oldVersion = ($currentVersion | Where-Object {$_.name -eq $s.name}).version
if ($s.version -ne $oldVersion) {
$repoUrl = ($repoUrl = $s.Url.Replace("/archive/", "/releases/")).Substring(0, $repoUrl.IndexOf("/releases/")) + "/releases"
# Store single dependency info for messages (only if this is the only update)
if ($count -eq 1) {
$singleDepName = $s.name
$singleDepOldVersion = $oldVersion
$singleDepNewVersion = $s.version
}
# Determine change type and emoji using shared function
$result = Get-VersionChangeType -OldVersion $oldVersion -NewVersion $s.version
$changeType = $result.ChangeType
$emoji = $result.Emoji
$isMajor = $result.IsMajor
# Track major updates for changelog section
if ($isMajor) {
$compareUrl = "$repoUrl/compare/v$oldVersion...v$($s.version)"
$majorUpdates += @{
name = $s.name
oldVersion = $oldVersion
newVersion = $s.version
compareUrl = $compareUrl
repoUrl = $repoUrl
}
}
$listUpdated += "$($s.name) v$($s.version), "
$updateMessage += "| $emoji **[$($s.name)]($repoUrl)** | ``$oldVersion`` | **``$($s.version)``** |`n"
}
}
if ($count -eq 0) { return }
Set-GHVariable -Name LIST_UPDATED -Value $listUpdated.Trim(', ')
# Set single dependency variables (they will only be used if COUNT_UPDATED is 1)
# Use safe fallback values in case variables weren't set (shouldn't happen but prevents errors)
if ([string]::IsNullOrEmpty($singleDepName) -and $count -eq 1) {
# This shouldn't happen, but if it does, log a warning
Write-Warning "Single dependency name not set despite count being 1"
$singleDepName = "unknown-package"
$singleDepOldVersion = "unknown"
$singleDepNewVersion = "unknown"
} elseif ([string]::IsNullOrEmpty($singleDepName)) {
# For multiple dependencies, set placeholder values (won't be used)
$singleDepName = ""
$singleDepOldVersion = ""
$singleDepNewVersion = ""
}
Set-GHVariable -Name SINGLE_DEP_NAME -Value $singleDepName
Set-GHVariable -Name SINGLE_DEP_OLD_VERSION -Value $singleDepOldVersion
Set-GHVariable -Name SINGLE_DEP_NEW_VERSION -Value $singleDepNewVersion
# Write multiline UPDATE_MESSAGE to GITHUB_ENV
## echo "UPDATE_MESSAGE<<EOF`n$updateMessage`nEOF" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
Add-Content -Path $env:GITHUB_ENV -Value "UPDATE_MESSAGE<<EOF"
Add-Content -Path $env:GITHUB_ENV -Value $updateMessage
Add-Content -Path $env:GITHUB_ENV -Value "EOF"
# Generate major updates changelog section and export
if ($majorUpdates.Count -gt 0) {
$changelogSection = "`n<details>`n<summary>🔥 Major version updates - View changelog</summary>`n`n"
foreach ($update in $majorUpdates) {
$changelogSection += "### [$($update.name)]($($update.repoUrl))`n"
$changelogSection += "**$($update.oldVersion)** → **$($update.newVersion)**`n`n"
$changelogSection += "- [View full changelog]($($update.compareUrl))`n"
$changelogSection += "- [Release notes]($($update.repoUrl)/tag/v$($update.newVersion))`n`n"
}
$changelogSection += "</details>`n"
Add-Content -Path $env:GITHUB_ENV -Value "CHANGELOG_SECTION<<EOF"
Add-Content -Path $env:GITHUB_ENV -Value $changelogSection
Add-Content -Path $env:GITHUB_ENV -Value "EOF"
Add-Content -Path $env:GITHUB_ENV -Value "HAS_BREAKING_CHANGES=True"
} else {
Add-Content -Path $env:GITHUB_ENV -Value "CHANGELOG_SECTION="
Add-Content -Path $env:GITHUB_ENV -Value "HAS_BREAKING_CHANGES=False"
}
- name: Summary - Update check results
shell: pwsh
run: |
$count = [int]$env:COUNT_UPDATED
if ($count -eq 0) {
$summary = "### ✅ No Updates Available`n`nAll vendor dependencies are up to date! 🎉`n"
} else {
$word = if ($count -eq 1) { 'dependency' } else { 'dependencies' }
$summary = "### 🔄 Updates Found`n`n"
if ($count -eq 1) {
$summary += '📦 **' + $env:SINGLE_DEP_NAME + '** updated from `' + $env:SINGLE_DEP_OLD_VERSION + '` to `' + $env:SINGLE_DEP_NEW_VERSION + '`' + "`n" + "`n"
} else {
$summary += '📦 **' + $count + '** vendor ' + $word + ' updated:' + "`n" + "`n"
}
}
$summary += $env:UPDATE_MESSAGE + "`n"
# Check if we can auto-merge (only minor/patch changes, opt-in only).
$hasBreaking = $env:HAS_BREAKING_CHANGES -eq 'True'
$autoMergeEnabled = $env:AUTO_MERGE_MINOR_VENDOR_UPDATES_ENABLED -eq 'True'
if ($hasBreaking) {
$summary += '> ⚠️ **Note:** This update contains major version changes that may include breaking changes.'
} elseif ($autoMergeEnabled) {
$summary += '> ℹ️ **Note:** This update only contains minor or patch changes, and auto-merge is enabled.'
} else {
$summary += '> ℹ️ **Note:** This update only contains minor or patch changes. Auto-merge is disabled by default; set the `AUTO_MERGE_MINOR_VENDOR_UPDATES` Actions repository variable to `true` to opt in.'
}
$summary | Add-Content -Path $env:GITHUB_STEP_SUMMARY -Encoding utf8
- name: Auto-merge minor updates
if: env.COUNT_UPDATED > 0 && env.HAS_BREAKING_CHANGES != 'True' && env.AUTO_MERGE_MINOR_VENDOR_UPDATES_ENABLED == 'True'
shell: pwsh
run: |
function Get-PushFailureReason {
param(
[string[]]$PushOutput
)
$text = ($PushOutput -join "`n").Trim()
if ([string]::IsNullOrWhiteSpace($text)) {
return "Git rejected the push to `master`."
}
if ($text -match 'GH006: Protected branch' -or $text -match 'protected branch hook declined') {
return "GitHub rejected the push because `master` is protected and direct pushes are blocked."
}
if ($text -match 'non-fast-forward|fetch first|failed to push some refs') {
return "The remote branch moved ahead of the local commit, so the push was rejected as non-fast-forward."
}
if ($text -match 'not authorized|permission denied|write access') {
return "The workflow token does not have permission to push to `master`."
}
return (($text -split "`r?`n" | Where-Object { $_ -match '^(remote:|! \[remote rejected\]|error:|To )' } | Select-Object -First 4) -join '; ')
}
try {
echo "### 🚀 Auto-merging Updates" >> $env:GITHUB_STEP_SUMMARY
echo "" >> $env:GITHUB_STEP_SUMMARY
echo "Attempting to automatically merge non-breaking changes to master..." >> $env:GITHUB_STEP_SUMMARY
git config --global user.name "github-actions[bot]"
git config --global user.email "github-actions[bot]@users.noreply.github.com"
# Commit the changes
git add vendor/sources.json
$commitResult = git commit -m "⬆️ Update dependencies ($env:LIST_UPDATED)"
$commitSuccess = $LASTEXITCODE -eq 0
if ($commitSuccess) {
# Push directly to master
$pushOutput = & git push origin HEAD:master 2>&1
$pushSuccess = $LASTEXITCODE -eq 0
if (-not $pushSuccess) {
$pushFailureReason = Get-PushFailureReason -PushOutput @($pushOutput)
if (-not [string]::IsNullOrWhiteSpace($pushFailureReason)) {
Add-Content -Path $env:GITHUB_ENV -Value "AUTO_MERGE_FAILURE_REASON=$pushFailureReason"
}
$pushErrorMessage = if ([string]::IsNullOrWhiteSpace($pushFailureReason)) {
"Failed to push to master (exit code: $LASTEXITCODE)"
} else {
"Failed to push to master (exit code: $LASTEXITCODE): $pushFailureReason"
}
}
if ($pushSuccess) {
echo "" >> $env:GITHUB_STEP_SUMMARY
echo "✅ **Success!** Updates have been automatically merged to master." >> $env:GITHUB_STEP_SUMMARY
echo "" >> $env:GITHUB_STEP_SUMMARY
echo "**Updated dependencies:** $env:LIST_UPDATED" >> $env:GITHUB_STEP_SUMMARY
# Set a flag to skip PR creation
echo "AUTO_MERGED=true" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
} else {
throw $pushErrorMessage
}
} else {
throw "Failed to commit changes (exit code: $LASTEXITCODE)"
}
} catch {
echo "" >> $env:GITHUB_STEP_SUMMARY
echo "⚠️ **Warning:** Unable to automatically merge updates." >> $env:GITHUB_STEP_SUMMARY
echo "" >> $env:GITHUB_STEP_SUMMARY
echo "**Error:** $($_.Exception.Message)" >> $env:GITHUB_STEP_SUMMARY
echo "" >> $env:GITHUB_STEP_SUMMARY
echo "Falling back to creating a pull request..." >> $env:GITHUB_STEP_SUMMARY
Write-Warning "Failed to auto-merge: $($_.Exception.Message)"
# Set flag to create PR instead
echo "AUTO_MERGED=false" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
$global:LASTEXITCODE = 0
exit 0
}
- id: create-pr
uses: peter-evans/create-pull-request@v8
if: fromJSON(env.COUNT_UPDATED) > 0 && env.AUTO_MERGED != 'true'
with:
title: ${{ env.COUNT_UPDATED == 1 && format('⬆️ Update {0}', env.LIST_UPDATED) || format('⬆️ Update {0} vendored dependencies', env.COUNT_UPDATED) }}
body: |
### ${{ env.COUNT_UPDATED == 1 && format('📦 Updated {0} from `{1}` to `{2}`', env.SINGLE_DEP_NAME, env.SINGLE_DEP_OLD_VERSION, env.SINGLE_DEP_NEW_VERSION) || format('📦 Automatically updated `{0}` dependencies', env.COUNT_UPDATED) }}
${{ env.UPDATE_MESSAGE }}
${{ env.CHANGELOG_SECTION }}
---
${{ env.HAS_BREAKING_CHANGES == 'True' && '⚠️ **This update contains major version changes that may include breaking changes.**' || 'ℹ️ This update only contains minor or patch changes.' }}
Please verify and then **Merge** the pull request to apply the updates.
commit-message: '⬆️ Update dependencies (${{ env.LIST_UPDATED }})'
branch: update-vendor
base: master
- name: Summary - Pull request result
if: env.COUNT_UPDATED > 0 && env.AUTO_MERGED != 'true'
shell: pwsh
env:
PR_URL: ${{ steps.create-pr.outputs.pull-request-url }}
PR_NUMBER: ${{ steps.create-pr.outputs.pull-request-number }}
PR_OPERATION: ${{ steps.create-pr.outputs.pull-request-operation }}
AUTO_MERGE_FAILURE_REASON: ${{ env.AUTO_MERGE_FAILURE_REASON }}
AUTO_MERGE_MINOR_VENDOR_UPDATES_ENABLED: ${{ env.AUTO_MERGE_MINOR_VENDOR_UPDATES_ENABLED }}
run: |
$updateVendorBranchUrl = "$($env:GITHUB_SERVER_URL)/$($env:GITHUB_REPOSITORY)/tree/update-vendor"
$summary = @"
### 🎉 Pull Request Result
**Branch:** [``update-vendor``]($updateVendorBranchUrl)
$(if (-not [string]::IsNullOrEmpty($env:LIST_UPDATED)) { "**Updated dependencies:** $env:LIST_UPDATED" } else { "**Updated dependencies:** " })
"@
switch ($env:PR_OPERATION) {
'created' {
$summary += "A new pull request has been created: [#$($env:PR_NUMBER)]($($env:PR_URL))`n`n"
}
'updated' {
$summary += "An existing pull request has been updated: [#$($env:PR_NUMBER)]($($env:PR_URL))`n`n"
}
'none' {
$summary += "No pull request was created because [update-vendor]($updateVendorBranchUrl) no longer differs from ``master``.`n`n"
}
default {
if (-not [string]::IsNullOrWhiteSpace($env:PR_URL)) {
$summary += "A pull request was reported, but the operation type was not recognized: [#$($env:PR_NUMBER)]($($env:PR_URL))`n`n"
} else {
$summary += "A pull request was requested, but no PR link was returned.`n`n"
}
}
}
if (-not [string]::IsNullOrWhiteSpace($env:AUTO_MERGE_FAILURE_REASON)) {
$summary += "> ⚠️ **Auto-merge failure:** $env:AUTO_MERGE_FAILURE_REASON`n`n"
}
if ($env:HAS_BREAKING_CHANGES -eq 'True') {
$summary += "> ⚠️ **Manual review required:** This update contains major version changes."
} elseif ($env:PR_OPERATION -eq 'none') {
$summary += "> ℹ️ **Note:** No pull request was created because there were no differences left to propose."
} elseif ($env:AUTO_MERGE_MINOR_VENDOR_UPDATES_ENABLED -ne 'True') {
$summary += "> ℹ️ **Note:** Auto-merge is disabled by configuration. Set the ``AUTO_MERGE_MINOR_VENDOR_UPDATES`` Actions repository variable to ``true`` to opt in."
} else {
$summary += "> ℹ️ **Note:** Auto-merge failed, manual review required."
}
$summary += "`n> Please review and merge the pull request to apply the updates.`n`n"
$summary | Add-Content -Path $env:GITHUB_STEP_SUMMARY -Encoding utf8