From ff0be355fc713257ea6faa5557d8fc970ed97a25 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Wed, 8 Oct 2025 15:00:55 +0200 Subject: [PATCH] feat(updater): Add post-update-script support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for running custom scripts after dependency updates to enable additional repository modifications before PR creation. Changes: - Add `post-update-script` input parameter to action.yml - Update update-dependency.ps1 to accept PostUpdateScript parameter - Scripts receive original and new version as arguments - Support both bash (.sh) and PowerShell (.ps1) scripts - Add comprehensive unit tests for post-update script functionality - Update README with usage examples for both script types The post-update script is executed after the dependency is updated but before the PR is created, allowing users to make additional changes such as updating lock files, running code generators, or modifying related configuration files. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CHANGELOG.md | 7 ++ updater/README.md | 45 ++++++++ updater/action.yml | 21 +++- updater/scripts/update-dependency.ps1 | 30 +++++- updater/tests/update-dependency.Tests.ps1 | 122 +++++++++++++++++++++- 5 files changed, 221 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d346e66..136d5fa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## Unreleased +### Features + +- Updater - Add `post-update-script` input parameter to run custom scripts after dependency updates ([#130](https://github.com/getsentry/github-workflows/pull/130)) + - Scripts receive original and new version as arguments + - Support both bash (`.sh`) and PowerShell (`.ps1`) scripts + - Enables workflows like updating lock files, running code generators, or modifying configuration files + ### Fixes - Updater - Fix boolean input handling for `changelog-entry` parameter and add input validation ([#127](https://github.com/getsentry/github-workflows/pull/127)) diff --git a/updater/README.md b/updater/README.md index 633af218..a25f4e54 100644 --- a/updater/README.md +++ b/updater/README.md @@ -95,6 +95,18 @@ jobs: target-branch: v7 pattern: '^1\.' # Limit to major version '1' api-token: ${{ secrets.CI_DEPLOY_KEY }} + + # Use a post-update script (sh or ps1) to make additional changes after dependency update + # The script receives two arguments: original version and new version + post-update-script: + runs-on: ubuntu-latest + steps: + - uses: getsentry/github-workflows/updater@v3 + with: + path: modules/sentry-cocoa + name: Cocoa SDK + post-update-script: scripts/post-update.sh # Receives args: $1=old version, $2=new version + api-token: ${{ secrets.CI_DEPLOY_KEY }} ``` ## Inputs @@ -135,12 +147,45 @@ jobs: * type: string * required: false * default: '' (uses repository default branch) +* `post-update-script`: Optional script to run after successful dependency update. Can be a bash script (`.sh`) or PowerShell script (`.ps1`). The script will be executed in the repository root directory before PR creation. The script receives two arguments: + * `$1` / `$args[0]` - The original version (version before update) + * `$2` / `$args[1]` - The new version (version after update) + * type: string + * required: false + * default: '' * `api-token`: Token for the repo. Can be passed in using `${{ secrets.GITHUB_TOKEN }}`. If you provide the usual `${{ github.token }}`, no followup CI will run on the created PR. If you want CI to run on the PRs created by the Updater, you need to provide custom user-specific auth token. * type: string * required: true +### Post-Update Script Example + +**Bash script** (`scripts/post-update.sh`): + +```bash +#!/usr/bin/env bash +set -euo pipefail + +ORIGINAL_VERSION="$1" +NEW_VERSION="$2" + +echo "Updated from $ORIGINAL_VERSION to $NEW_VERSION" +# Make additional changes to repository files here +``` + +**PowerShell script** (`scripts/post-update.ps1`): + +```powershell +param( + [Parameter(Mandatory = $true)][string] $OriginalVersion, + [Parameter(Mandatory = $true)][string] $NewVersion +) + +Write-Output "Updated from $OriginalVersion to $NewVersion" +# Make additional changes to repository files here +``` + ## Outputs * `prUrl`: The created/updated PR's URL. diff --git a/updater/action.yml b/updater/action.yml index a21da6c0..5349ec98 100644 --- a/updater/action.yml +++ b/updater/action.yml @@ -36,6 +36,10 @@ inputs: api-token: description: 'Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}' required: true + post-update-script: + description: 'Optional script to run after successful dependency update. Can be a bash script (.sh) or PowerShell script (.ps1). The script will be executed in the caller-repo directory before PR creation.' + required: false + default: '' outputs: prUrl: @@ -102,6 +106,17 @@ runs: } Write-Output "✓ PR strategy value '${{ inputs.pr-strategy }}' is valid" + - name: Validate post-update-script + if: ${{ inputs.post-update-script != '' }} + shell: pwsh + run: | + # Validate that inputs.post-update-script contains only safe characters + if ('${{ inputs.post-update-script }}' -notmatch '^[a-zA-Z0-9_\./#\s-]+$') { + Write-Output "::error::Invalid post-update-script path: '${{ inputs.post-update-script }}'. Only alphanumeric characters, spaces, and _-./# are allowed." + exit 1 + } + Write-Output "✓ Post-update script path '${{ inputs.post-update-script }}' is valid" + # What we need to accomplish: # * update to the latest tag # * create a PR @@ -134,8 +149,9 @@ runs: DEPENDENCY_PATH: ${{ inputs.path }} DEPENDENCY_PATTERN: ${{ inputs.pattern }} GH_TITLE_PATTERN: ${{ inputs.gh-title-pattern }} + POST_UPDATE_SCRIPT: ${{ inputs.post-update-script }} GH_TOKEN: ${{ inputs.api-token }} - run: ${{ github.action_path }}/scripts/update-dependency.ps1 -Path $env:DEPENDENCY_PATH -Pattern $env:DEPENDENCY_PATTERN -GhTitlePattern $env:GH_TITLE_PATTERN + run: ${{ github.action_path }}/scripts/update-dependency.ps1 -Path $env:DEPENDENCY_PATH -Pattern $env:DEPENDENCY_PATTERN -GhTitlePattern $env:GH_TITLE_PATTERN -PostUpdateScript $env:POST_UPDATE_SCRIPT - name: Get the base repo info if: steps.target.outputs.latestTag != steps.target.outputs.originalTag @@ -270,8 +286,9 @@ runs: working-directory: caller-repo env: DEPENDENCY_PATH: ${{ inputs.path }} + POST_UPDATE_SCRIPT: ${{ inputs.post-update-script }} GH_TOKEN: ${{ inputs.api-token }} - run: ${{ github.action_path }}/scripts/update-dependency.ps1 -Path $env:DEPENDENCY_PATH -Tag '${{ steps.target.outputs.latestTag }}' + run: ${{ github.action_path }}/scripts/update-dependency.ps1 -Path $env:DEPENDENCY_PATH -Tag '${{ steps.target.outputs.latestTag }}' -PostUpdateScript $env:POST_UPDATE_SCRIPT - name: Update Changelog if: ${{ inputs.changelog-entry == 'true' && ( steps.target.outputs.latestTag != steps.target.outputs.originalTag ) && ( steps.root.outputs.changed == 'false') }} diff --git a/updater/scripts/update-dependency.ps1 b/updater/scripts/update-dependency.ps1 index c8258581..648a3a89 100644 --- a/updater/scripts/update-dependency.ps1 +++ b/updater/scripts/update-dependency.ps1 @@ -15,7 +15,10 @@ param( # RegEx pattern to match against GitHub release titles. Only releases with matching titles will be considered [string] $GhTitlePattern = '', # Specific version - if passed, no discovery is performed and the version is set directly - [string] $Tag = '' + [string] $Tag = '', + # Optional post-update script to run after successful dependency update + # The script receives the original and new version as arguments + [string] $PostUpdateScript = '' ) $ErrorActionPreference = 'Stop' @@ -249,6 +252,9 @@ if ("$Tag" -eq '') { $Tag = $latestTag } +$originalTagForPostUpdate = if ($originalTag) { $originalTag } else { '' } +$newTagForPostUpdate = $Tag + if ($isSubmodule) { Write-Host "Updating submodule $Path to $Tag" Push-Location $Path @@ -258,3 +264,25 @@ if ($isSubmodule) { Write-Host "Updating 'version' in $Path to $Tag" DependencyConfig 'set-version' $tag } + +# Run post-update script if provided +if ("$PostUpdateScript" -ne '') { + Write-Host "Running post-update script: $PostUpdateScript" + if (-not (Test-Path $PostUpdateScript)) { + throw "Post-update script not found: $PostUpdateScript" + } + + if (Get-Command 'chmod' -ErrorAction SilentlyContinue) { + chmod +x $PostUpdateScript + if ($LastExitCode -ne 0) { + throw 'chmod failed'; + } + } + + & $PostUpdateScript "$originalTag" "$tag" + if ($LastExitCode -ne 0) { + throw "Post-update script failed with exit code $LastExitCode" + } + + Write-Host '✓ Post-update script completed successfully' +} diff --git a/updater/tests/update-dependency.Tests.ps1 b/updater/tests/update-dependency.Tests.ps1 index 6b77a426..0d92a02a 100644 --- a/updater/tests/update-dependency.Tests.ps1 +++ b/updater/tests/update-dependency.Tests.ps1 @@ -1,9 +1,10 @@ BeforeAll { - function UpdateDependency([Parameter(Mandatory = $true)][string] $path, [string] $pattern = $null, [string] $ghTitlePattern = $null) + function UpdateDependency([Parameter(Mandatory = $true)][string] $path, [string] $pattern = $null, [string] $ghTitlePattern = $null, [string] $postUpdateScript = $null) { $params = @{ Path = $path } if ($pattern) { $params.Pattern = $pattern } if ($ghTitlePattern) { $params.GhTitlePattern = $ghTitlePattern } + if ($postUpdateScript) { $params.PostUpdateScript = $postUpdateScript } $result = & "$PSScriptRoot/../scripts/update-dependency.ps1" @params if (-not $?) @@ -488,4 +489,123 @@ FetchContent_Declare( $version | Should -Match '^8\.' } } + + Context 'post-update-script' { + It 'runs PowerShell post-update script with version arguments' { + $testFile = "$testDir/test.properties" + $repo = 'https://github.com/getsentry/sentry-cli' + @("repo=$repo", 'version=0') | Out-File $testFile + + $postUpdateScript = "$testDir/post-update-test.ps1" + $markerFile = "$testDir/post-update-marker.txt" + @' +param([string] $originalVersion, [string] $newVersion) +"$originalVersion|$newVersion" | Out-File +'@ + " '$markerFile'" | Out-File $postUpdateScript + + UpdateDependency $testFile '^0\.' -postUpdateScript $postUpdateScript + + # Verify post-update script was executed + Test-Path $markerFile | Should -Be $true + $markerContent = Get-Content $markerFile + $markerContent | Should -Match '^0\|0\.28\.0$' + + # Clean up + Remove-Item $markerFile -ErrorAction SilentlyContinue + Remove-Item $postUpdateScript -ErrorAction SilentlyContinue + } + + It 'runs bash post-update script with version arguments' -Skip:$IsWindows { + $testFile = "$testDir/test.properties" + $repo = 'https://github.com/getsentry/sentry-cli' + @("repo=$repo", 'version=0') | Out-File $testFile + + $postUpdateScript = "$testDir/post-update-test.sh" + $markerFile = "$testDir/post-update-marker.txt" + @" +#!/usr/bin/env bash +set -euo pipefail +echo "`$1|`$2" > '$markerFile' +"@ | Out-File $postUpdateScript + + UpdateDependency $testFile '^0\.' -postUpdateScript $postUpdateScript + + # Verify post-update script was executed + Test-Path $markerFile | Should -Be $true + $markerContent = Get-Content $markerFile + $markerContent | Should -Match '^0\|0\.28\.0$' + + # Clean up + Remove-Item $markerFile -ErrorAction SilentlyContinue + Remove-Item $postUpdateScript -ErrorAction SilentlyContinue + } + + It 'fails when post-update script does not exist' { + $testFile = "$testDir/test.properties" + $repo = 'https://github.com/getsentry/sentry-cli' + @("repo=$repo", 'version=0') | Out-File $testFile + + $postUpdateScript = "$testDir/nonexistent-script.ps1" + + { UpdateDependency $testFile '^0\.' -postUpdateScript $postUpdateScript } | Should -Throw '*Post-update script not found*' + } + + It 'fails when PowerShell post-update script exits with error' { + $testFile = "$testDir/test.properties" + $repo = 'https://github.com/getsentry/sentry-cli' + @("repo=$repo", 'version=0') | Out-File $testFile + + $postUpdateScript = "$testDir/failing-post-update.ps1" + @' +param([string] $originalVersion, [string] $newVersion) +throw "Post-update script failed intentionally" +'@ | Out-File $postUpdateScript + + { UpdateDependency $testFile '^0\.' -postUpdateScript $postUpdateScript } | Should -Throw '*Post-update script failed*' + + # Clean up + Remove-Item $postUpdateScript -ErrorAction SilentlyContinue + } + + It 'fails when bash post-update script exits with error' -Skip:$IsWindows { + $testFile = "$testDir/test.properties" + $repo = 'https://github.com/getsentry/sentry-cli' + @("repo=$repo", 'version=0') | Out-File $testFile + + $postUpdateScript = "$testDir/failing-post-update.sh" + @' +#!/usr/bin/env bash +exit 1 +'@ | Out-File $postUpdateScript + + { UpdateDependency $testFile '^0\.' -postUpdateScript $postUpdateScript } | Should -Throw '*Post-update script failed*' + + # Clean up + Remove-Item $postUpdateScript -ErrorAction SilentlyContinue + } + + It 'receives empty string for original version when updating from scratch' { + $testFile = "$testDir/test.properties" + $repo = 'https://github.com/getsentry/sentry-cli' + @("repo=$repo", 'version=') | Out-File $testFile + + $postUpdateScript = "$testDir/post-update-empty-original.ps1" + $markerFile = "$testDir/post-update-marker-empty.txt" + @' +param([string] $originalVersion, [string] $newVersion) +"original=[$originalVersion]|new=[$newVersion]" | Out-File +'@ + " '$markerFile'" | Out-File $postUpdateScript + + UpdateDependency $testFile '^0\.' -postUpdateScript $postUpdateScript + + # Verify post-update script received empty original version + Test-Path $markerFile | Should -Be $true + $markerContent = Get-Content $markerFile + $markerContent | Should -Match 'original=\[\]\|new=\[0\.28\.0\]' + + # Clean up + Remove-Item $markerFile -ErrorAction SilentlyContinue + Remove-Item $postUpdateScript -ErrorAction SilentlyContinue + } + } }