diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..6911141 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,88 @@ +# Font Data Updater + +This directory contains scripts for automating the maintenance of the NerdFonts module. + +## Update-FontsData.ps1 + +This script automatically updates the `src/FontsData.json` file with the latest font metadata from the +[ryanoasis/nerd-fonts](https://github.com/ryanoasis/nerd-fonts) repository. + +### Features + +- **Automatic Updates**: Runs daily via GitHub Actions to fetch the latest font data +- **PR Supersedence**: Automatically closes older update pull requests when a new update is created +- **Clean Repository**: Ensures only the most recent update PR remains open + +### How It Works + +1. **Scheduled Execution**: The script runs daily at midnight UTC via the `Update-FontsData` workflow +2. **Data Fetching**: Retrieves the latest font release metadata from the NerdFonts repository +3. **Change Detection**: Compares new data with existing `FontsData.json` +4. **PR Creation**: If changes are detected: + - Creates a new branch named `auto-update-YYYYMMDD-HHmmss` + - Commits the updated `FontsData.json` + - Opens a pull request with title `Auto-Update YYYYMMDD-HHmmss` +5. **PR Supersedence**: After creating a new PR, the script: + - Searches for existing open PRs with titles matching `Auto-Update*` (excluding the newly created PR) + - Closes each superseded PR with a comment referencing the new PR number + - Deletes the branches associated with superseded PRs + - Ensures only the latest update PR remains open + +### PR Lifecycle Management + +The font data updater implements PR supersedence similar to Dependabot. When a new update PR is created: + +- The script first creates the new PR +- Then checks for existing open `Auto-Update*` PRs (excluding the newly created one) +- Each existing PR receives a comment referencing the new PR number: + + ```text + This PR has been superseded by #[NEW_PR_NUMBER] and will be closed automatically. + + The font data has been updated in the newer PR. Please refer to #[NEW_PR_NUMBER] for the most current changes. + ``` + +- All superseded PRs are automatically closed +- Branches for closed PRs are deleted + +This means there is no need for a separate cleanup workflow on merge — by the time a PR is merged, +it is already the only open Auto-Update PR. + +### Workflow + +#### Update-FontsData.yml + +Handles the scheduled updates, PR creation, and supersedence: + +- **Trigger**: Daily at midnight UTC, or manual via `workflow_dispatch` +- **Authentication**: Uses GitHub App credentials for API access + +### Manual Execution + +You can manually trigger an update using the GitHub Actions UI: + +1. Go to the **Actions** tab in the repository +2. Select the **Update-FontsData** workflow +3. Click **Run workflow** +4. Select the branch and click **Run workflow** + +### Configuration + +The supersedence behavior is built into the script and requires no additional configuration. The message +posted when closing superseded PRs can be customized by modifying `scripts/Update-FontsData.ps1`. + +### Development + +To test changes to the update script: + +1. Create a feature branch +2. Modify `scripts/Update-FontsData.ps1` +3. Push the branch +4. Manually trigger the workflow on your feature branch +5. The script will detect it's running on a feature branch and update the existing branch instead of + creating a new PR + +### Troubleshooting + +- **No updates available**: If the NerdFonts release contains the same data, no PR will be created +- **Authentication errors**: Ensure the GitHub App credentials are correctly configured diff --git a/scripts/Update-FontsData.ps1 b/scripts/Update-FontsData.ps1 index ea748b9..4e113a6 100644 --- a/scripts/Update-FontsData.ps1 +++ b/scripts/Update-FontsData.ps1 @@ -33,6 +33,8 @@ } } +$repoName = $env:GITHUB_REPOSITORY + Install-PSResource -Repository PSGallery -TrustRepository -Name 'Json' Connect-GitHubApp -Organization 'PSModule' -Default @@ -125,5 +127,77 @@ LogGroup 'Process changes' { --body 'This PR updates FontsData.json with the latest metadata.' Write-Output "Changes detected and PR opened for branch: $targetBranch" + + # Close any existing open Auto-Update PRs after creating the new one + LogGroup 'Close superseded PRs' { + Write-Output 'Checking for existing open Auto-Update PRs to supersede...' + + # Get the newly created PR with retry logic + $newPRJson = $null + $retryCount = 0 + $maxRetries = 3 + $retryDelays = @(1, 2, 3) # Progressive delays in seconds + while ($null -eq $newPRJson -and $retryCount -lt $maxRetries) { + if ($retryCount -gt 0) { + Start-Sleep -Seconds $retryDelays[$retryCount - 1] + } + $newPRJson = Run gh pr list --repo $repoName --head $targetBranch --state open --json 'number,title' --limit 1 + $newPR = $newPRJson | ConvertFrom-Json | Select-Object -First 1 + if ($null -eq $newPR -or $null -eq $newPR.number) { + $newPR = $null + $newPRJson = $null + } + $retryCount++ + if ($null -eq $newPR -and $retryCount -lt $maxRetries) { + Write-Output "PR not found yet, retrying in $($retryDelays[$retryCount - 1]) seconds... (attempt $retryCount/$maxRetries)" + } + } + + if ($null -ne $newPR) { + Write-Output "Found new PR #$($newPR.number): $($newPR.title)" + + # Find existing open Auto-Update PRs (excluding the one we just created) + $existingPRsJson = Run gh pr list --repo $repoName --state open --search 'Auto-Update in:title' --json 'number,title,headRefName' + $existingPRs = $existingPRsJson | ConvertFrom-Json | Where-Object { $_.number -ne $newPR.number } + + if ($existingPRs) { + Write-Output "Found $(@($existingPRs).Count) existing Auto-Update PR(s) to close." + foreach ($pr in $existingPRs) { + Write-Output "Closing PR #$($pr.number): $($pr.title)" + + # Add a comment explaining the supersedence + $comment = @" +This PR has been superseded by #$($newPR.number) and will be closed automatically. + +The font data has been updated in the newer PR. Please refer to #$($newPR.number) for the most current changes. +"@ + Run gh pr comment $pr.number --repo $repoName --body $comment + + # Close the PR + Run gh pr close $pr.number --repo $repoName + + Write-Output "Successfully closed PR #$($pr.number)" + + # Delete the branch associated with the closed PR + $branchName = $pr.headRefName + if ($branchName) { + Write-Output "Deleting branch: $branchName" + $null = Run gh api -X DELETE "repos/$repoName/git/refs/heads/$branchName" + if ($LASTEXITCODE -eq 0) { + Write-Output "Successfully deleted branch: $branchName" + } else { + Write-Warning "Failed to delete branch $branchName (exit code $LASTEXITCODE)" + } + } else { + Write-Warning "Could not determine branch name for PR #$($pr.number)" + } + } + } else { + Write-Output 'No existing open Auto-Update PRs to close.' + } + } else { + Write-Warning "Could not retrieve the newly created PR after $maxRetries attempts. Skipping supersedence logic." + } + } } }