From 0954f8958328e60cb078e1cd1b47013d056ed59c Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 18 Sep 2025 22:38:01 +0200 Subject: [PATCH 01/21] feat: Add CMake FetchContent support to updater MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements support for updating CMake FetchContent_Declare() statements in addition to existing submodules, properties files, and scripts. Key features: - Support for path.cmake#DepName and auto-detection syntax - Hash vs tag detection with hash format preservation - Hash-to-tag resolution for version comparison - GitHub Actions output integration - Comprehensive test coverage (23 tests) Resolves: https://github.com/getsentry/github-workflows/issues/91 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- updater/scripts/cmake-functions.ps1 | 97 ++++++++++ updater/scripts/update-dependency.ps1 | 46 ++++- .../testdata/cmake/complex-formatting.cmake | 14 ++ .../testdata/cmake/hash-dependency.cmake | 11 ++ updater/tests/testdata/cmake/malformed.cmake | 8 + .../testdata/cmake/missing-repository.cmake | 8 + .../cmake/multiple-dependencies.cmake | 17 ++ .../testdata/cmake/single-dependency.cmake | 11 ++ .../tests/update-dependency-cmake.Tests.ps1 | 167 +++++++++++++++++ updater/tests/update-dependency.Tests.ps1 | 176 ++++++++++++++++++ 10 files changed, 554 insertions(+), 1 deletion(-) create mode 100644 updater/scripts/cmake-functions.ps1 create mode 100644 updater/tests/testdata/cmake/complex-formatting.cmake create mode 100644 updater/tests/testdata/cmake/hash-dependency.cmake create mode 100644 updater/tests/testdata/cmake/malformed.cmake create mode 100644 updater/tests/testdata/cmake/missing-repository.cmake create mode 100644 updater/tests/testdata/cmake/multiple-dependencies.cmake create mode 100644 updater/tests/testdata/cmake/single-dependency.cmake create mode 100644 updater/tests/update-dependency-cmake.Tests.ps1 diff --git a/updater/scripts/cmake-functions.ps1 b/updater/scripts/cmake-functions.ps1 new file mode 100644 index 00000000..c9f34c8d --- /dev/null +++ b/updater/scripts/cmake-functions.ps1 @@ -0,0 +1,97 @@ +# CMake FetchContent helper functions for update-dependency.ps1 + +function Parse-CMakeFetchContent($filePath, $depName) { + $content = Get-Content $filePath -Raw + + if ($depName) { + $pattern = "FetchContent_Declare\s*\(\s*$depName\s+([^)]+)\)" + } else { + # Find all FetchContent_Declare blocks + $allMatches = [regex]::Matches($content, "FetchContent_Declare\s*\(\s*([a-zA-Z0-9_-]+)", 'Singleline') + if ($allMatches.Count -eq 1) { + $depName = $allMatches[0].Groups[1].Value + $pattern = "FetchContent_Declare\s*\(\s*$depName\s+([^)]+)\)" + } else { + throw "Multiple FetchContent declarations found. Use #DepName syntax." + } + } + + $match = [regex]::Match($content, $pattern, 'Singleline,IgnoreCase') + if (-not $match.Success) { + throw "FetchContent_Declare for '$depName' not found in $filePath" + } + $block = $match.Groups[1].Value + + # Look for GIT_REPOSITORY and GIT_TAG patterns specifically + # Exclude matches that are in comments (lines starting with #) + $repoMatch = [regex]::Match($block, '(?m)^\s*GIT_REPOSITORY\s+(\S+)') + $tagMatch = [regex]::Match($block, '(?m)^\s*GIT_TAG\s+(\S+)') + + $repo = if ($repoMatch.Success) { $repoMatch.Groups[1].Value } else { "" } + $tag = if ($tagMatch.Success) { $tagMatch.Groups[1].Value } else { "" } + + if ([string]::IsNullOrEmpty($repo) -or [string]::IsNullOrEmpty($tag)) { + throw "Could not parse GIT_REPOSITORY or GIT_TAG from FetchContent_Declare block" + } + + return @{ GitRepository = $repo; GitTag = $tag; DepName = $depName } +} + +function Find-TagForHash($repo, $hash) { + try { + $refs = git ls-remote --tags $repo + foreach ($ref in $refs) { + $commit, $tagRef = $ref -split '\s+', 2 + if ($commit -eq $hash) { + return $tagRef -replace '^refs/tags/', '' + } + } + return $null + } + catch { + Write-Host "Warning: Could not resolve hash $hash to tag name: $_" + return $null + } +} + +function Update-CMakeFile($filePath, $depName, $newValue) { + $content = Get-Content $filePath -Raw + $fetchContent = Parse-CMakeFetchContent $filePath $depName + $originalValue = $fetchContent.GitTag + $repo = $fetchContent.GitRepository + $wasHash = $originalValue -match '^[a-f0-9]{40}$' + + if ($wasHash) { + # Convert tag to hash and add comment + $newHashRefs = git ls-remote $repo "refs/tags/$newValue" + if (-not $newHashRefs) { + throw "Tag $newValue not found in repository $repo" + } + $newHash = ($newHashRefs -split '\s+')[0] + $replacement = "$newHash # $newValue" + + # Validate ancestry: ensure old hash is reachable from new tag + # Note: Skipping ancestry check for now as it requires local repository + # TODO: Implement proper ancestry validation for remote repositories + Write-Host "Warning: Skipping ancestry validation for hash update from $originalValue to $newValue" + } else { + $replacement = $newValue + } + + # Update GIT_TAG value, preserving formatting + $pattern = "(FetchContent_Declare\s*\(\s*$depName\s+[^)]*GIT_TAG\s+)\S+([^#\r\n]*).*?(\s*[^)]*\))" + $newContent = [regex]::Replace($content, $pattern, "`${1}$replacement`${3}", 'Singleline') + + if ($newContent -eq $content) { + throw "Failed to update GIT_TAG in $filePath - pattern may not have matched" + } + + $newContent | Out-File $filePath -NoNewline + + # Verify the update worked + $verifyContent = Parse-CMakeFetchContent $filePath $depName + $expectedValue = $wasHash ? $newHash : $newValue + if ($verifyContent.GitTag -notmatch [regex]::Escape($expectedValue)) { + throw "Update verification failed - read-after-write did not match expected value" + } +} \ No newline at end of file diff --git a/updater/scripts/update-dependency.ps1 b/updater/scripts/update-dependency.ps1 index c9af07b0..18fca8d4 100644 --- a/updater/scripts/update-dependency.ps1 +++ b/updater/scripts/update-dependency.ps1 @@ -6,6 +6,9 @@ param( # * `get-version` - return the currently specified dependency version # * `get-repo` - return the repository url (e.g. https://github.com/getsentry/dependency) # * `set-version` - update the dependency version (passed as another string argument after this one) + # - a CMake file (.cmake) with FetchContent_Declare statements: + # * Use `path/to/file.cmake#DepName` to specify dependency name + # * Or just `path/to/file.cmake` if file contains single FetchContent_Declare [Parameter(Mandatory = $true)][string] $Path, # RegEx pattern that will be matched against available versions when picking the latest one [string] $Pattern = '', @@ -16,6 +19,19 @@ param( Set-StrictMode -Version latest . "$PSScriptRoot/common.ps1" +# Parse CMake file with dependency name +$cmakeFile = $null +$cmakeDep = $null +if ($Path -match '^(.+\.cmake)#(.+)$') { + $cmakeFile = $Matches[1] + $cmakeDep = $Matches[2] + $Path = $cmakeFile # Set Path to file for existing logic +} elseif ($Path -match '\.cmake$') { + $cmakeFile = $Path + $cmakeDep = $null # Will auto-detect +} +$isCMakeFile = $cmakeFile -ne $null + if (-not (Test-Path $Path )) { throw "Dependency $Path doesn't exit"; @@ -41,7 +57,32 @@ if (-not $isSubmodule) $isScript = $Path -match '\.(ps1|sh)$' function DependencyConfig ([Parameter(Mandatory = $true)][string] $action, [string] $value = $null) { - if ($isScript) + if ($isCMakeFile) { + # CMake file handling + switch ($action) { + 'get-version' { + $fetchContent = Parse-CMakeFetchContent $cmakeFile $cmakeDep + $currentValue = $fetchContent.GitTag + if ($currentValue -match '^[a-f0-9]{40}$') { + # Try to resolve hash to tag for version comparison + $repo = $fetchContent.GitRepository + $tagForHash = Find-TagForHash $repo $currentValue + return $tagForHash ?? $currentValue + } + return $currentValue + } + 'get-repo' { + return (Parse-CMakeFetchContent $cmakeFile $cmakeDep).GitRepository + } + 'set-version' { + Update-CMakeFile $cmakeFile $cmakeDep $value + } + Default { + throw "Unknown action $action" + } + } + } + elseif ($isScript) { if (Get-Command 'chmod' -ErrorAction SilentlyContinue) { @@ -99,6 +140,9 @@ if (-not $isSubmodule) } } } + + # Load CMake helper functions + . "$PSScriptRoot/cmake-functions.ps1" } if ("$Tag" -eq '') diff --git a/updater/tests/testdata/cmake/complex-formatting.cmake b/updater/tests/testdata/cmake/complex-formatting.cmake new file mode 100644 index 00000000..94ed6c54 --- /dev/null +++ b/updater/tests/testdata/cmake/complex-formatting.cmake @@ -0,0 +1,14 @@ +include(FetchContent) + +FetchContent_Declare(sentry-native + GIT_REPOSITORY + https://github.com/getsentry/sentry-native + GIT_TAG + v0.9.1 # Current stable version + GIT_SHALLOW + FALSE + GIT_SUBMODULES + "external/breakpad" +) + +FetchContent_MakeAvailable(sentry-native) \ No newline at end of file diff --git a/updater/tests/testdata/cmake/hash-dependency.cmake b/updater/tests/testdata/cmake/hash-dependency.cmake new file mode 100644 index 00000000..baa8fc99 --- /dev/null +++ b/updater/tests/testdata/cmake/hash-dependency.cmake @@ -0,0 +1,11 @@ +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2 # 0.9.1 + GIT_SHALLOW FALSE + GIT_SUBMODULES "external/breakpad" +) + +FetchContent_MakeAvailable(sentry-native) \ No newline at end of file diff --git a/updater/tests/testdata/cmake/malformed.cmake b/updater/tests/testdata/cmake/malformed.cmake new file mode 100644 index 00000000..bff0196d --- /dev/null +++ b/updater/tests/testdata/cmake/malformed.cmake @@ -0,0 +1,8 @@ +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + # Missing GIT_TAG + GIT_SHALLOW FALSE +) \ No newline at end of file diff --git a/updater/tests/testdata/cmake/missing-repository.cmake b/updater/tests/testdata/cmake/missing-repository.cmake new file mode 100644 index 00000000..fcdbaf08 --- /dev/null +++ b/updater/tests/testdata/cmake/missing-repository.cmake @@ -0,0 +1,8 @@ +include(FetchContent) + +FetchContent_Declare( + sentry-native + # Missing GIT_REPOSITORY + GIT_TAG v0.9.1 + GIT_SHALLOW FALSE +) \ No newline at end of file diff --git a/updater/tests/testdata/cmake/multiple-dependencies.cmake b/updater/tests/testdata/cmake/multiple-dependencies.cmake new file mode 100644 index 00000000..aa8c0c94 --- /dev/null +++ b/updater/tests/testdata/cmake/multiple-dependencies.cmake @@ -0,0 +1,17 @@ +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG v0.9.1 + GIT_SHALLOW FALSE +) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest + GIT_TAG v1.14.0 + EXCLUDE_FROM_ALL +) + +FetchContent_MakeAvailable(sentry-native googletest) \ No newline at end of file diff --git a/updater/tests/testdata/cmake/single-dependency.cmake b/updater/tests/testdata/cmake/single-dependency.cmake new file mode 100644 index 00000000..30279bfb --- /dev/null +++ b/updater/tests/testdata/cmake/single-dependency.cmake @@ -0,0 +1,11 @@ +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG v0.9.1 + GIT_SHALLOW FALSE + GIT_SUBMODULES "external/breakpad" +) + +FetchContent_MakeAvailable(sentry-native) \ No newline at end of file diff --git a/updater/tests/update-dependency-cmake.Tests.ps1 b/updater/tests/update-dependency-cmake.Tests.ps1 new file mode 100644 index 00000000..c5229117 --- /dev/null +++ b/updater/tests/update-dependency-cmake.Tests.ps1 @@ -0,0 +1,167 @@ +BeforeAll { + # Load CMake helper functions from the main script + . "$PSScriptRoot/../scripts/cmake-functions.ps1" + + $testDataDir = "$PSScriptRoot/testdata/cmake" +} + +Describe 'CMake Helper Functions' { + Context 'Parse-CMakeFetchContent' { + It 'parses basic FetchContent_Declare with explicit dependency name' { + $testFile = "$testDataDir/single-dependency.cmake" + + $result = Parse-CMakeFetchContent $testFile 'sentry-native' + + $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' + $result.GitTag | Should -Be 'v0.9.1' + $result.DepName | Should -Be 'sentry-native' + } + + It 'auto-detects single FetchContent_Declare' { + $testFile = "$testDataDir/single-dependency.cmake" + + $result = Parse-CMakeFetchContent $testFile $null + + $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' + $result.GitTag | Should -Be 'v0.9.1' + $result.DepName | Should -Be 'sentry-native' + } + + It 'handles hash values correctly' { + $testFile = "$testDataDir/hash-dependency.cmake" + + $result = Parse-CMakeFetchContent $testFile 'sentry-native' + + $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' + $result.GitTag | Should -Be 'a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2' + $result.DepName | Should -Be 'sentry-native' + } + + It 'handles complex multi-line formatting' { + $testFile = "$testDataDir/complex-formatting.cmake" + + $result = Parse-CMakeFetchContent $testFile 'sentry-native' + + $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' + $result.GitTag | Should -Be 'v0.9.1' + $result.DepName | Should -Be 'sentry-native' + } + + It 'throws on multiple dependencies without explicit name' { + $testFile = "$testDataDir/multiple-dependencies.cmake" + + { Parse-CMakeFetchContent $testFile $null } | Should -Throw '*Multiple FetchContent declarations found*' + } + + It 'handles specific dependency from multiple dependencies' { + $testFile = "$testDataDir/multiple-dependencies.cmake" + + $result = Parse-CMakeFetchContent $testFile 'googletest' + + $result.GitRepository | Should -Be 'https://github.com/google/googletest' + $result.GitTag | Should -Be 'v1.14.0' + $result.DepName | Should -Be 'googletest' + } + + It 'throws on missing dependency' { + $testFile = "$testDataDir/single-dependency.cmake" + + { Parse-CMakeFetchContent $testFile 'nonexistent' } | Should -Throw "*FetchContent_Declare for 'nonexistent' not found*" + } + + It 'throws on missing GIT_REPOSITORY' { + $testFile = "$testDataDir/missing-repository.cmake" + + { Parse-CMakeFetchContent $testFile 'sentry-native' } | Should -Throw '*Could not parse GIT_REPOSITORY or GIT_TAG*' + } + + It 'throws on missing GIT_TAG' { + $testFile = "$testDataDir/malformed.cmake" + + { Parse-CMakeFetchContent $testFile 'sentry-native' } | Should -Throw '*Could not parse GIT_REPOSITORY or GIT_TAG*' + } + } + + Context 'Find-TagForHash' { + It 'returns null for hash without matching tag' { + # Use a fake hash that won't match any real tag + $fakeHash = 'abcdef1234567890abcdef1234567890abcdef12' + $repo = 'https://github.com/getsentry/sentry-native' + + $result = Find-TagForHash $repo $fakeHash + + $result | Should -BeNullOrEmpty + } + + It 'handles network failures gracefully' { + $invalidRepo = 'https://github.com/nonexistent/repo' + $hash = 'abcdef1234567890abcdef1234567890abcdef12' + + # Should not throw, but return null and show warning + $result = Find-TagForHash $invalidRepo $hash + + $result | Should -BeNullOrEmpty + } + + # Note: Testing actual hash resolution requires network access + # and is better suited for integration tests + } + + Context 'Update-CMakeFile' { + BeforeEach { + # Create a temporary copy of test files for modification + $script:tempDir = "$TestDrive/cmake-tests" + New-Item $tempDir -ItemType Directory -Force | Out-Null + } + + It 'updates tag to tag preserving format' { + $sourceFile = "$testDataDir/single-dependency.cmake" + $testFile = "$tempDir/test.cmake" + Copy-Item $sourceFile $testFile + + Update-CMakeFile $testFile 'sentry-native' 'v0.9.2' + + $content = Get-Content $testFile -Raw + $content | Should -Match 'GIT_TAG v0.9.2' + $content | Should -Not -Match 'v0.9.1' + } + + It 'preserves file structure and other content' { + $sourceFile = "$testDataDir/single-dependency.cmake" + $testFile = "$tempDir/test.cmake" + Copy-Item $sourceFile $testFile + + Update-CMakeFile $testFile 'sentry-native' 'v0.9.2' + + $content = Get-Content $testFile -Raw + $content | Should -Match 'include\(FetchContent\)' + $content | Should -Match 'FetchContent_MakeAvailable' + $content | Should -Match 'GIT_REPOSITORY https://github.com/getsentry/sentry-native' + $content | Should -Match 'GIT_SHALLOW FALSE' + } + + It 'handles complex formatting correctly' { + $sourceFile = "$testDataDir/complex-formatting.cmake" + $testFile = "$tempDir/test.cmake" + Copy-Item $sourceFile $testFile + + Update-CMakeFile $testFile 'sentry-native' 'v0.9.2' + + $content = Get-Content $testFile -Raw + $content | Should -Match 'GIT_TAG\s+v0.9.2' + $content | Should -Not -Match 'v0.9.1' + } + + It 'throws on failed regex match' { + $sourceFile = "$testDataDir/single-dependency.cmake" + $testFile = "$tempDir/test.cmake" + Copy-Item $sourceFile $testFile + + # Try to update a dependency that doesn't exist + { Update-CMakeFile $testFile 'nonexistent-dep' 'v1.0.0' } | Should -Throw "*FetchContent_Declare for 'nonexistent-dep' not found*" + } + + # Note: Hash update tests require network access for git ls-remote + # and are better suited for integration tests + } +} diff --git a/updater/tests/update-dependency.Tests.ps1 b/updater/tests/update-dependency.Tests.ps1 index 3d1c9fe0..9d4fecb5 100644 --- a/updater/tests/update-dependency.Tests.ps1 +++ b/updater/tests/update-dependency.Tests.ps1 @@ -247,4 +247,180 @@ switch ($action) } } } + + Context 'cmake-fetchcontent' { + BeforeAll { + $cmakeTestDir = "$testDir/cmake" + if (-not (Test-Path $cmakeTestDir)) { + New-Item $cmakeTestDir -ItemType Directory + } + } + + It 'updates CMake file with explicit dependency name' { + $testFile = "$cmakeTestDir/sentry-explicit.cmake" + @' +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG v0.9.1 + GIT_SHALLOW FALSE +) + +FetchContent_MakeAvailable(sentry-native) +'@ | Out-File $testFile + + UpdateDependency "$testFile#sentry-native" + + $content = Get-Content $testFile -Raw + $content | Should -Not -Match 'v0.9.1' + $content | Should -Match 'GIT_TAG \d+\.\d+\.\d+' + $content | Should -Match 'GIT_REPOSITORY https://github.com/getsentry/sentry-native' + } + + It 'auto-detects single FetchContent dependency' { + $testFile = "$cmakeTestDir/sentry-auto.cmake" + @' +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG v0.9.0 + GIT_SHALLOW FALSE +) + +FetchContent_MakeAvailable(sentry-native) +'@ | Out-File $testFile + + UpdateDependency $testFile + + $content = Get-Content $testFile -Raw + $content | Should -Not -Match 'v0.9.0' + $content | Should -Match 'GIT_TAG \d+\.\d+\.\d+' + } + + It 'updates from hash to newer tag preserving hash format' { + $testFile = "$cmakeTestDir/sentry-hash.cmake" + @' +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2 # 0.9.1 + GIT_SHALLOW FALSE +) + +FetchContent_MakeAvailable(sentry-native) +'@ | Out-File $testFile + + UpdateDependency $testFile + + $content = Get-Content $testFile -Raw + # Should update to a new hash with tag comment (note: may have comment formatting issues) + $content | Should -Match 'GIT_TAG [a-f0-9]{40} # \d+\.\d+\.\d+' + $content | Should -Not -Match 'a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2' + } + + It 'handles multiple dependencies with explicit selection' { + $testFile = "$cmakeTestDir/multiple-deps.cmake" + @' +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG v0.9.1 +) + +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest + GIT_TAG v1.14.0 +) + +FetchContent_MakeAvailable(sentry-native googletest) +'@ | Out-File $testFile + + UpdateDependency "$testFile#googletest" + + $content = Get-Content $testFile -Raw + # sentry-native should remain unchanged + $content | Should -Match 'sentry-native[\s\S]*GIT_TAG v0\.9\.1' + # googletest should be updated + $content | Should -Match 'googletest[\s\S]*GIT_TAG v1\.\d+\.\d+' + $content | Should -Not -Match 'googletest[\s\S]*GIT_TAG v1\.14\.0' + } + + It 'outputs correct GitHub Actions variables' { + $testFile = "$cmakeTestDir/output-test.cmake" + @' +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG v0.9.0 +) +'@ | Out-File $testFile + + $output = UpdateDependency $testFile + + # Join output lines for easier searching + $outputText = $output -join "`n" + $outputText | Should -Match 'originalTag=v0\.9\.0' + $outputText | Should -Match 'latestTag=\d+\.\d+\.\d+' + $outputText | Should -Match 'url=https://github.com/getsentry/sentry-native' + $outputText | Should -Match 'mainBranch=master' + } + + It 'respects version patterns' { + $testFile = "$cmakeTestDir/pattern-test.cmake" + @' +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG v0.8.0 +) +'@ | Out-File $testFile + + # Limit to 0.9.x versions + UpdateDependency $testFile '^v?0\.9\.' + + $content = Get-Content $testFile -Raw + $content | Should -Match 'GIT_TAG 0\.9\.\d+' + $content | Should -Not -Match 'v0\.8\.0' + } + + It 'fails on multiple dependencies without explicit name' { + $testFile = "$cmakeTestDir/multi-fail.cmake" + @' +include(FetchContent) + +FetchContent_Declare(sentry-native GIT_REPOSITORY https://github.com/getsentry/sentry-native GIT_TAG v0.9.1) +FetchContent_Declare(googletest GIT_REPOSITORY https://github.com/google/googletest GIT_TAG v1.14.0) +'@ | Out-File $testFile + + { UpdateDependency $testFile } | Should -Throw '*Multiple FetchContent declarations found*' + } + + It 'fails on missing dependency' { + $testFile = "$cmakeTestDir/missing-dep.cmake" + @' +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG v0.9.1 +) +'@ | Out-File $testFile + + { UpdateDependency "$testFile#nonexistent" } | Should -Throw "*FetchContent_Declare for 'nonexistent' not found*" + } + } } From 891ea75684a03df77808a083fbf11cfd99b1c492 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 18 Sep 2025 22:45:22 +0200 Subject: [PATCH 02/21] fix: Complete CMake FetchContent implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Critical fixes and improvements: - Fix GitHub Actions workflow validation to allow # character in paths - Update documentation with CMake examples and usage - Improve comment handling in hash updates - Implement proper ancestry validation for hash updates - Test with real console SDK CMake files 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .github/workflows/updater.yml | 8 ++--- README.md | 21 +++++++++++- updater/scripts/cmake-functions.ps1 | 51 ++++++++++++++++++++++++++--- 3 files changed, 70 insertions(+), 10 deletions(-) diff --git a/.github/workflows/updater.yml b/.github/workflows/updater.yml index b56434da..e98a257f 100644 --- a/.github/workflows/updater.yml +++ b/.github/workflows/updater.yml @@ -3,7 +3,7 @@ on: workflow_call: inputs: path: - description: Dependency path in the source repository, this can be either a submodule, a .properties file or a shell script. + description: Dependency path in the source repository, this can be either a submodule, a .properties file, a shell script, or a CMake file with FetchContent. type: string required: true name: @@ -87,9 +87,9 @@ jobs: - name: Validate dependency path shell: pwsh run: | - # Validate that inputs.path contains only safe characters - if ('${{ inputs.path }}' -notmatch '^[a-zA-Z0-9_\./-]+$') { - Write-Output "::error::Invalid dependency path: '${{ inputs.path }}'. Only alphanumeric characters and _-./ are allowed." + # Validate that inputs.path contains only safe characters (including # for CMake dependencies) + if ('${{ inputs.path }}' -notmatch '^[a-zA-Z0-9_\./#-]+$') { + Write-Output "::error::Invalid dependency path: '${{ inputs.path }}'. Only alphanumeric characters and _-./# are allowed." exit 1 } Write-Output "✓ Dependency path '${{ inputs.path }}' is valid" diff --git a/README.md b/README.md index 48d4e1bc..6355ed96 100644 --- a/README.md +++ b/README.md @@ -46,11 +46,30 @@ jobs: name: Gradle Plugin secrets: api-token: ${{ secrets.CI_DEPLOY_KEY }} + + # Update a CMake FetchContent dependency with explicit dependency name + sentry-native: + uses: getsentry/github-workflows/.github/workflows/updater.yml@v2 + with: + path: vendor/sentry-native.cmake#sentry-native + name: Sentry Native SDK + secrets: + api-token: ${{ secrets.CI_DEPLOY_KEY }} + + # Update a CMake FetchContent dependency with auto-detection (single dependency only) + deps: + uses: getsentry/github-workflows/.github/workflows/updater.yml@v2 + with: + path: vendor/dependencies.cmake + name: Dependencies + secrets: + api-token: ${{ secrets.CI_DEPLOY_KEY }} ``` ### Inputs -* `path`: Dependency path in the source repository, this can be either a submodule, a .properties file or a shell script. +* `path`: Dependency path in the source repository, this can be either a submodule, a .properties file, a shell script, or a CMake file with FetchContent. + * For CMake files: Use `path/to/file.cmake#DepName` to specify dependency name, or just `path/to/file.cmake` for auto-detection (single dependency only) * type: string * required: true * `name`: Name used in the PR title and the changelog entry. diff --git a/updater/scripts/cmake-functions.ps1 b/updater/scripts/cmake-functions.ps1 index c9f34c8d..6394f05f 100644 --- a/updater/scripts/cmake-functions.ps1 +++ b/updater/scripts/cmake-functions.ps1 @@ -54,6 +54,41 @@ function Find-TagForHash($repo, $hash) { } } +function Test-HashAncestry($repo, $oldHash, $newHash) { + try { + # Create a temporary directory for git operations + $tempDir = Join-Path $env:TEMP "git-ancestry-check-$(Get-Random)" + New-Item -ItemType Directory -Path $tempDir -Force | Out-Null + + try { + Push-Location $tempDir + + # Initialize a bare repository and add the remote + git init --bare 2>$null | Out-Null + git remote add origin $repo 2>$null | Out-Null + + # Fetch both commits + git fetch origin $oldHash 2>$null | Out-Null + git fetch origin $newHash 2>$null | Out-Null + + # Check if old hash is ancestor of new hash + git merge-base --is-ancestor $oldHash $newHash 2>$null + $isAncestor = $LastExitCode -eq 0 + + return $isAncestor + } + finally { + Pop-Location + Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue + } + } + catch { + Write-Host "Warning: Could not validate ancestry for $oldHash -> $newHash : $_" + # When in doubt, allow the update (safer for automation) + return $true + } +} + function Update-CMakeFile($filePath, $depName, $newValue) { $content = Get-Content $filePath -Raw $fetchContent = Parse-CMakeFetchContent $filePath $depName @@ -71,16 +106,22 @@ function Update-CMakeFile($filePath, $depName, $newValue) { $replacement = "$newHash # $newValue" # Validate ancestry: ensure old hash is reachable from new tag - # Note: Skipping ancestry check for now as it requires local repository - # TODO: Implement proper ancestry validation for remote repositories - Write-Host "Warning: Skipping ancestry validation for hash update from $originalValue to $newValue" + if (-not (Test-HashAncestry $repo $originalValue $newHash)) { + throw "Cannot update: hash $originalValue is not in history of tag $newValue" + } } else { $replacement = $newValue } # Update GIT_TAG value, preserving formatting - $pattern = "(FetchContent_Declare\s*\(\s*$depName\s+[^)]*GIT_TAG\s+)\S+([^#\r\n]*).*?(\s*[^)]*\))" - $newContent = [regex]::Replace($content, $pattern, "`${1}$replacement`${3}", 'Singleline') + if ($wasHash) { + # For hash updates, replace everything after GIT_TAG on that line (including existing comments) + $pattern = "(FetchContent_Declare\s*\(\s*$depName\s+[^)]*GIT_TAG\s+)[^\r\n]+(\r?\n[^)]*\))" + } else { + # For tag updates, preserve existing structure + $pattern = "(FetchContent_Declare\s*\(\s*$depName\s+[^)]*GIT_TAG\s+)\S+(\s*[^)]*\))" + } + $newContent = [regex]::Replace($content, $pattern, "`${1}$replacement`${2}", 'Singleline') if ($newContent -eq $content) { throw "Failed to update GIT_TAG in $filePath - pattern may not have matched" From 9566917ee9434ad81b8498e257a8b9b4b37fca74 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 18 Sep 2025 22:50:37 +0200 Subject: [PATCH 03/21] docs: Fix CMake examples to be more logical - sentry-native.cmake now uses auto-detection (single dependency) - dependencies.cmake now shows explicit dependency name syntax - Better reflects real-world usage patterns --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6355ed96..83a6146e 100644 --- a/README.md +++ b/README.md @@ -47,21 +47,21 @@ jobs: secrets: api-token: ${{ secrets.CI_DEPLOY_KEY }} - # Update a CMake FetchContent dependency with explicit dependency name + # Update a CMake FetchContent dependency with auto-detection (single dependency only) sentry-native: uses: getsentry/github-workflows/.github/workflows/updater.yml@v2 with: - path: vendor/sentry-native.cmake#sentry-native + path: vendor/sentry-native.cmake name: Sentry Native SDK secrets: api-token: ${{ secrets.CI_DEPLOY_KEY }} - # Update a CMake FetchContent dependency with auto-detection (single dependency only) + # Update a CMake FetchContent dependency with explicit dependency name deps: uses: getsentry/github-workflows/.github/workflows/updater.yml@v2 with: - path: vendor/dependencies.cmake - name: Dependencies + path: vendor/dependencies.cmake#googletest + name: GoogleTest secrets: api-token: ${{ secrets.CI_DEPLOY_KEY }} ``` From ca1444d46839e82c92aff7948f1d6faca32d52fa Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 18 Sep 2025 22:51:35 +0200 Subject: [PATCH 04/21] docs: Refactor path input description into cleaner sublist - Split long bullet point into structured sublist - Clear separation of different path format types - Better readability for CMake file options --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 83a6146e..1b5a6c5b 100644 --- a/README.md +++ b/README.md @@ -68,8 +68,13 @@ jobs: ### Inputs -* `path`: Dependency path in the source repository, this can be either a submodule, a .properties file, a shell script, or a CMake file with FetchContent. - * For CMake files: Use `path/to/file.cmake#DepName` to specify dependency name, or just `path/to/file.cmake` for auto-detection (single dependency only) +* `path`: Dependency path in the source repository. Supported formats: + * Submodule path + * Properties file (`.properties`) + * Shell script (`.ps1`, `.sh`) + * CMake file with FetchContent: + * `path/to/file.cmake#DepName` - specify dependency name + * `path/to/file.cmake` - auto-detection (single dependency only) * type: string * required: true * `name`: Name used in the PR title and the changelog entry. From c676be002644d234325552daa90067328cd3259e Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 18 Sep 2025 22:58:20 +0200 Subject: [PATCH 05/21] fix: cleanup CMake file handling in update-dependency script --- updater/scripts/update-dependency.ps1 | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/updater/scripts/update-dependency.ps1 b/updater/scripts/update-dependency.ps1 index 18fca8d4..6b3f4379 100644 --- a/updater/scripts/update-dependency.ps1 +++ b/updater/scripts/update-dependency.ps1 @@ -20,17 +20,18 @@ Set-StrictMode -Version latest . "$PSScriptRoot/common.ps1" # Parse CMake file with dependency name -$cmakeFile = $null -$cmakeDep = $null -if ($Path -match '^(.+\.cmake)#(.+)$') { - $cmakeFile = $Matches[1] - $cmakeDep = $Matches[2] - $Path = $cmakeFile # Set Path to file for existing logic -} elseif ($Path -match '\.cmake$') { - $cmakeFile = $Path - $cmakeDep = $null # Will auto-detect +if ($Path -match '^(.+\.cmake)(#.+)?$') { + $Path = $Matches[1] # Set Path to file for existing logic + if ($Matches[2]) { + $cmakeDep = $Matches[2].TrimStart('#') + } else { + $cmakeDep = $null # Will auto-detect + } + $isCMakeFile = $true +} else { + $cmakeDep = $null + $isCMakeFile = $false } -$isCMakeFile = $cmakeFile -ne $null if (-not (Test-Path $Path )) { @@ -61,7 +62,7 @@ if (-not $isSubmodule) # CMake file handling switch ($action) { 'get-version' { - $fetchContent = Parse-CMakeFetchContent $cmakeFile $cmakeDep + $fetchContent = Parse-CMakeFetchContent $Path $cmakeDep $currentValue = $fetchContent.GitTag if ($currentValue -match '^[a-f0-9]{40}$') { # Try to resolve hash to tag for version comparison @@ -72,10 +73,10 @@ if (-not $isSubmodule) return $currentValue } 'get-repo' { - return (Parse-CMakeFetchContent $cmakeFile $cmakeDep).GitRepository + return (Parse-CMakeFetchContent $Path $cmakeDep).GitRepository } 'set-version' { - Update-CMakeFile $cmakeFile $cmakeDep $value + Update-CMakeFile $Path $cmakeDep $value } Default { throw "Unknown action $action" From e11ac8105232736d3b2b268bcd612677ff76a80d Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 18 Sep 2025 23:04:16 +0200 Subject: [PATCH 06/21] fix: ensure newline at end of file in Update-CMakeFile function --- updater/scripts/cmake-functions.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updater/scripts/cmake-functions.ps1 b/updater/scripts/cmake-functions.ps1 index 6394f05f..798c3d17 100644 --- a/updater/scripts/cmake-functions.ps1 +++ b/updater/scripts/cmake-functions.ps1 @@ -135,4 +135,4 @@ function Update-CMakeFile($filePath, $depName, $newValue) { if ($verifyContent.GitTag -notmatch [regex]::Escape($expectedValue)) { throw "Update verification failed - read-after-write did not match expected value" } -} \ No newline at end of file +} From 0cea8934ef4379993477a615abf0e926b0fa4d9b Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 18 Sep 2025 23:05:11 +0200 Subject: [PATCH 07/21] refactor: Use cross-platform temp directory approach - Replace $env:TEMP with [System.IO.Path]::GetTempPath() - Use [System.Guid]::NewGuid() for unique directory names - More robust cross-platform compatibility --- updater/scripts/cmake-functions.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/updater/scripts/cmake-functions.ps1 b/updater/scripts/cmake-functions.ps1 index 798c3d17..2ab128c5 100644 --- a/updater/scripts/cmake-functions.ps1 +++ b/updater/scripts/cmake-functions.ps1 @@ -57,7 +57,7 @@ function Find-TagForHash($repo, $hash) { function Test-HashAncestry($repo, $oldHash, $newHash) { try { # Create a temporary directory for git operations - $tempDir = Join-Path $env:TEMP "git-ancestry-check-$(Get-Random)" + $tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid()) New-Item -ItemType Directory -Path $tempDir -Force | Out-Null try { From e0b5a399b381dffd2b5b7668221cc315ee4c570c Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 18 Sep 2025 23:07:44 +0200 Subject: [PATCH 08/21] security: Fix ancestry validation to fail safely - Return false instead of true when ancestry validation fails - Change warning to error message for clarity - Prevents potentially incorrect updates when validation is uncertain - Follows fail-safe principle for security-critical operations --- updater/scripts/cmake-functions.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/updater/scripts/cmake-functions.ps1 b/updater/scripts/cmake-functions.ps1 index 2ab128c5..3b188643 100644 --- a/updater/scripts/cmake-functions.ps1 +++ b/updater/scripts/cmake-functions.ps1 @@ -83,9 +83,9 @@ function Test-HashAncestry($repo, $oldHash, $newHash) { } } catch { - Write-Host "Warning: Could not validate ancestry for $oldHash -> $newHash : $_" - # When in doubt, allow the update (safer for automation) - return $true + Write-Host "Error: Could not validate ancestry for $oldHash -> $newHash : $_" + # When in doubt, fail safely to prevent incorrect updates + return $false } } From b9c8c4f5a4f8a70e389ad5c2de4e1d0c79c69394 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 18 Sep 2025 23:11:09 +0200 Subject: [PATCH 09/21] refactor: Simplify GIT_TAG line replacement logic - Always replace entire line content after GIT_TAG - Removes potentially outdated version-specific comments - Simplifies regex pattern (no separate hash/tag logic needed) - Cleaner and more predictable behavior --- updater/scripts/cmake-functions.ps1 | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/updater/scripts/cmake-functions.ps1 b/updater/scripts/cmake-functions.ps1 index 3b188643..64937ba9 100644 --- a/updater/scripts/cmake-functions.ps1 +++ b/updater/scripts/cmake-functions.ps1 @@ -113,14 +113,9 @@ function Update-CMakeFile($filePath, $depName, $newValue) { $replacement = $newValue } - # Update GIT_TAG value, preserving formatting - if ($wasHash) { - # For hash updates, replace everything after GIT_TAG on that line (including existing comments) - $pattern = "(FetchContent_Declare\s*\(\s*$depName\s+[^)]*GIT_TAG\s+)[^\r\n]+(\r?\n[^)]*\))" - } else { - # For tag updates, preserve existing structure - $pattern = "(FetchContent_Declare\s*\(\s*$depName\s+[^)]*GIT_TAG\s+)\S+(\s*[^)]*\))" - } + # Update GIT_TAG value, replacing entire line content after GIT_TAG + # This removes potentially outdated version-specific comments + $pattern = "(FetchContent_Declare\s*\(\s*$depName\s+[^)]*GIT_TAG\s+)[^\r\n]+(\r?\n[^)]*\))" $newContent = [regex]::Replace($content, $pattern, "`${1}$replacement`${2}", 'Singleline') if ($newContent -eq $content) { From 8c0107a733ce1c2108643f4b2507b7ba921201dd Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 18 Sep 2025 23:15:14 +0200 Subject: [PATCH 10/21] fix: Add proper error handling for git ls-remote commands - Check $LASTEXITCODE after git ls-remote calls - Prevent parsing error messages as commit hashes - Fixes potential corruption of CMake files with 'fatal:' etc. - Applies to both Update-CMakeFile and Find-TagForHash functions Fixes critical bug where network failures could corrupt dependency files. --- updater/scripts/cmake-functions.ps1 | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/updater/scripts/cmake-functions.ps1 b/updater/scripts/cmake-functions.ps1 index 64937ba9..7e7d4e6f 100644 --- a/updater/scripts/cmake-functions.ps1 +++ b/updater/scripts/cmake-functions.ps1 @@ -40,6 +40,9 @@ function Parse-CMakeFetchContent($filePath, $depName) { function Find-TagForHash($repo, $hash) { try { $refs = git ls-remote --tags $repo + if ($LASTEXITCODE -ne 0) { + throw "Failed to fetch tags from repository $repo (git ls-remote failed with exit code $LASTEXITCODE)" + } foreach ($ref in $refs) { $commit, $tagRef = $ref -split '\s+', 2 if ($commit -eq $hash) { @@ -99,6 +102,9 @@ function Update-CMakeFile($filePath, $depName, $newValue) { if ($wasHash) { # Convert tag to hash and add comment $newHashRefs = git ls-remote $repo "refs/tags/$newValue" + if ($LASTEXITCODE -ne 0) { + throw "Failed to fetch tag $newValue from repository $repo (git ls-remote failed with exit code $LASTEXITCODE)" + } if (-not $newHashRefs) { throw "Tag $newValue not found in repository $repo" } From f05851420a4573e3a3bc61cd0c88ecfe8ddf6bd8 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Thu, 18 Sep 2025 23:24:00 +0200 Subject: [PATCH 11/21] test: Add missing hash-to-hash update test case - Tests updating from one git hash to a newer tag's hash - Covers important scenario of hash-to-hash updates - Verifies hash format preservation and comment replacement - Ensures old hash and comments are properly removed --- updater/tests/update-dependency-cmake.Tests.ps1 | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/updater/tests/update-dependency-cmake.Tests.ps1 b/updater/tests/update-dependency-cmake.Tests.ps1 index c5229117..976d87eb 100644 --- a/updater/tests/update-dependency-cmake.Tests.ps1 +++ b/updater/tests/update-dependency-cmake.Tests.ps1 @@ -126,6 +126,22 @@ Describe 'CMake Helper Functions' { $content | Should -Not -Match 'v0.9.1' } + It 'updates hash to newer hash preserving format' { + $sourceFile = "$testDataDir/hash-dependency.cmake" + $testFile = "$tempDir/test.cmake" + Copy-Item $sourceFile $testFile + + # Update to a newer tag that will be converted to hash (0.11.0 is known to exist) + Update-CMakeFile $testFile 'sentry-native' '0.11.0' + + $content = Get-Content $testFile -Raw + # Should have new hash with tag comment + $content | Should -Match 'GIT_TAG [a-f0-9]{40} # 0.11.0' + # Should not have old hash or old comment + $content | Should -Not -Match 'a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2' + $content | Should -Not -Match '# 0.9.1' + } + It 'preserves file structure and other content' { $sourceFile = "$testDataDir/single-dependency.cmake" $testFile = "$tempDir/test.cmake" From 2f572cc72bdce8591494c48c81291e80ec52f22b Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 19 Sep 2025 08:48:48 +0200 Subject: [PATCH 12/21] refactor: Inline test data and group related test cases - Move CMake test data from external files to inline here-strings - Group related test scenarios into single test cases for better readability - Reduce test count from 16 to 6 while maintaining same coverage - Remove external testdata/cmake/ directory (no longer needed) - Improve test maintainability - all test input/output visible in one place Test groupings: - Parse scenarios: basic, auto-detect, hash, complex formatting - Multiple deps: auto-detection errors, explicit selection - Error scenarios: missing deps, missing repo/tag - Hash resolution: null results, network failures - Update scenarios: tag-to-tag, hash-to-hash, complex formatting - Update errors: missing dependency updates --- .../testdata/cmake/complex-formatting.cmake | 14 - .../testdata/cmake/hash-dependency.cmake | 11 - updater/tests/testdata/cmake/malformed.cmake | 8 - .../testdata/cmake/missing-repository.cmake | 8 - .../cmake/multiple-dependencies.cmake | 17 -- .../testdata/cmake/single-dependency.cmake | 11 - .../tests/update-dependency-cmake.Tests.ps1 | 288 ++++++++++++------ 7 files changed, 196 insertions(+), 161 deletions(-) delete mode 100644 updater/tests/testdata/cmake/complex-formatting.cmake delete mode 100644 updater/tests/testdata/cmake/hash-dependency.cmake delete mode 100644 updater/tests/testdata/cmake/malformed.cmake delete mode 100644 updater/tests/testdata/cmake/missing-repository.cmake delete mode 100644 updater/tests/testdata/cmake/multiple-dependencies.cmake delete mode 100644 updater/tests/testdata/cmake/single-dependency.cmake diff --git a/updater/tests/testdata/cmake/complex-formatting.cmake b/updater/tests/testdata/cmake/complex-formatting.cmake deleted file mode 100644 index 94ed6c54..00000000 --- a/updater/tests/testdata/cmake/complex-formatting.cmake +++ /dev/null @@ -1,14 +0,0 @@ -include(FetchContent) - -FetchContent_Declare(sentry-native - GIT_REPOSITORY - https://github.com/getsentry/sentry-native - GIT_TAG - v0.9.1 # Current stable version - GIT_SHALLOW - FALSE - GIT_SUBMODULES - "external/breakpad" -) - -FetchContent_MakeAvailable(sentry-native) \ No newline at end of file diff --git a/updater/tests/testdata/cmake/hash-dependency.cmake b/updater/tests/testdata/cmake/hash-dependency.cmake deleted file mode 100644 index baa8fc99..00000000 --- a/updater/tests/testdata/cmake/hash-dependency.cmake +++ /dev/null @@ -1,11 +0,0 @@ -include(FetchContent) - -FetchContent_Declare( - sentry-native - GIT_REPOSITORY https://github.com/getsentry/sentry-native - GIT_TAG a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2 # 0.9.1 - GIT_SHALLOW FALSE - GIT_SUBMODULES "external/breakpad" -) - -FetchContent_MakeAvailable(sentry-native) \ No newline at end of file diff --git a/updater/tests/testdata/cmake/malformed.cmake b/updater/tests/testdata/cmake/malformed.cmake deleted file mode 100644 index bff0196d..00000000 --- a/updater/tests/testdata/cmake/malformed.cmake +++ /dev/null @@ -1,8 +0,0 @@ -include(FetchContent) - -FetchContent_Declare( - sentry-native - GIT_REPOSITORY https://github.com/getsentry/sentry-native - # Missing GIT_TAG - GIT_SHALLOW FALSE -) \ No newline at end of file diff --git a/updater/tests/testdata/cmake/missing-repository.cmake b/updater/tests/testdata/cmake/missing-repository.cmake deleted file mode 100644 index fcdbaf08..00000000 --- a/updater/tests/testdata/cmake/missing-repository.cmake +++ /dev/null @@ -1,8 +0,0 @@ -include(FetchContent) - -FetchContent_Declare( - sentry-native - # Missing GIT_REPOSITORY - GIT_TAG v0.9.1 - GIT_SHALLOW FALSE -) \ No newline at end of file diff --git a/updater/tests/testdata/cmake/multiple-dependencies.cmake b/updater/tests/testdata/cmake/multiple-dependencies.cmake deleted file mode 100644 index aa8c0c94..00000000 --- a/updater/tests/testdata/cmake/multiple-dependencies.cmake +++ /dev/null @@ -1,17 +0,0 @@ -include(FetchContent) - -FetchContent_Declare( - sentry-native - GIT_REPOSITORY https://github.com/getsentry/sentry-native - GIT_TAG v0.9.1 - GIT_SHALLOW FALSE -) - -FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest - GIT_TAG v1.14.0 - EXCLUDE_FROM_ALL -) - -FetchContent_MakeAvailable(sentry-native googletest) \ No newline at end of file diff --git a/updater/tests/testdata/cmake/single-dependency.cmake b/updater/tests/testdata/cmake/single-dependency.cmake deleted file mode 100644 index 30279bfb..00000000 --- a/updater/tests/testdata/cmake/single-dependency.cmake +++ /dev/null @@ -1,11 +0,0 @@ -include(FetchContent) - -FetchContent_Declare( - sentry-native - GIT_REPOSITORY https://github.com/getsentry/sentry-native - GIT_TAG v0.9.1 - GIT_SHALLOW FALSE - GIT_SUBMODULES "external/breakpad" -) - -FetchContent_MakeAvailable(sentry-native) \ No newline at end of file diff --git a/updater/tests/update-dependency-cmake.Tests.ps1 b/updater/tests/update-dependency-cmake.Tests.ps1 index 976d87eb..c4a04b70 100644 --- a/updater/tests/update-dependency-cmake.Tests.ps1 +++ b/updater/tests/update-dependency-cmake.Tests.ps1 @@ -1,105 +1,177 @@ BeforeAll { # Load CMake helper functions from the main script . "$PSScriptRoot/../scripts/cmake-functions.ps1" - - $testDataDir = "$PSScriptRoot/testdata/cmake" } Describe 'CMake Helper Functions' { Context 'Parse-CMakeFetchContent' { - It 'parses basic FetchContent_Declare with explicit dependency name' { - $testFile = "$testDataDir/single-dependency.cmake" + BeforeEach { + $script:tempDir = "$TestDrive/cmake-tests" + New-Item $tempDir -ItemType Directory -Force | Out-Null + } + + It 'parses FetchContent_Declare with various scenarios' { + # Test 1: Basic parsing with explicit dependency name + $testFile1 = "$tempDir/basic.cmake" + @' +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG v0.9.1 + GIT_SHALLOW FALSE +) - $result = Parse-CMakeFetchContent $testFile 'sentry-native' +FetchContent_MakeAvailable(sentry-native) +'@ | Out-File $testFile1 + $result = Parse-CMakeFetchContent $testFile1 'sentry-native' $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' $result.GitTag | Should -Be 'v0.9.1' $result.DepName | Should -Be 'sentry-native' - } - - It 'auto-detects single FetchContent_Declare' { - $testFile = "$testDataDir/single-dependency.cmake" - - $result = Parse-CMakeFetchContent $testFile $null + # Test 2: Auto-detection with same file + $result = Parse-CMakeFetchContent $testFile1 $null $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' $result.GitTag | Should -Be 'v0.9.1' $result.DepName | Should -Be 'sentry-native' - } - It 'handles hash values correctly' { - $testFile = "$testDataDir/hash-dependency.cmake" + # Test 3: Hash values + $testFile2 = "$tempDir/hash.cmake" + @' +include(FetchContent) - $result = Parse-CMakeFetchContent $testFile 'sentry-native' +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2 # 0.9.1 + GIT_SHALLOW FALSE + GIT_SUBMODULES "external/breakpad" +) +FetchContent_MakeAvailable(sentry-native) +'@ | Out-File $testFile2 + + $result = Parse-CMakeFetchContent $testFile2 'sentry-native' $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' $result.GitTag | Should -Be 'a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2' $result.DepName | Should -Be 'sentry-native' - } - - It 'handles complex multi-line formatting' { - $testFile = "$testDataDir/complex-formatting.cmake" - - $result = Parse-CMakeFetchContent $testFile 'sentry-native' + # Test 4: Complex multi-line formatting + $testFile3 = "$tempDir/complex.cmake" + @' +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY + https://github.com/getsentry/sentry-native + GIT_TAG + v0.9.1 + GIT_SHALLOW + FALSE + GIT_SUBMODULES + "external/breakpad" +) + +FetchContent_MakeAvailable(sentry-native) +'@ | Out-File $testFile3 + + $result = Parse-CMakeFetchContent $testFile3 'sentry-native' $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' $result.GitTag | Should -Be 'v0.9.1' $result.DepName | Should -Be 'sentry-native' } - It 'throws on multiple dependencies without explicit name' { - $testFile = "$testDataDir/multiple-dependencies.cmake" + It 'handles multiple dependencies correctly' { + $testFile = "$tempDir/multiple.cmake" + @' +include(FetchContent) - { Parse-CMakeFetchContent $testFile $null } | Should -Throw '*Multiple FetchContent declarations found*' - } +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG v0.9.1 +) - It 'handles specific dependency from multiple dependencies' { - $testFile = "$testDataDir/multiple-dependencies.cmake" +FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest + GIT_TAG v1.14.0 +) - $result = Parse-CMakeFetchContent $testFile 'googletest' +FetchContent_MakeAvailable(sentry-native googletest) +'@ | Out-File $testFile + # Should throw when no explicit name given + { Parse-CMakeFetchContent $testFile $null } | Should -Throw '*Multiple FetchContent declarations found*' + + # Should work with explicit dependency name + $result = Parse-CMakeFetchContent $testFile 'googletest' $result.GitRepository | Should -Be 'https://github.com/google/googletest' $result.GitTag | Should -Be 'v1.14.0' $result.DepName | Should -Be 'googletest' } - It 'throws on missing dependency' { - $testFile = "$testDataDir/single-dependency.cmake" - - { Parse-CMakeFetchContent $testFile 'nonexistent' } | Should -Throw "*FetchContent_Declare for 'nonexistent' not found*" - } - - It 'throws on missing GIT_REPOSITORY' { - $testFile = "$testDataDir/missing-repository.cmake" - - { Parse-CMakeFetchContent $testFile 'sentry-native' } | Should -Throw '*Could not parse GIT_REPOSITORY or GIT_TAG*' - } - - It 'throws on missing GIT_TAG' { - $testFile = "$testDataDir/malformed.cmake" - - { Parse-CMakeFetchContent $testFile 'sentry-native' } | Should -Throw '*Could not parse GIT_REPOSITORY or GIT_TAG*' + It 'throws appropriate errors for invalid scenarios' { + # Test 1: Missing dependency + $testFile1 = "$tempDir/valid.cmake" + @' +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG v0.9.1 +) +'@ | Out-File $testFile1 + + { Parse-CMakeFetchContent $testFile1 'nonexistent' } | Should -Throw "*FetchContent_Declare for 'nonexistent' not found*" + + # Test 2: Missing GIT_REPOSITORY + $testFile2 = "$tempDir/missing-repo.cmake" + @' +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_TAG v0.9.1 +) +'@ | Out-File $testFile2 + + { Parse-CMakeFetchContent $testFile2 'sentry-native' } | Should -Throw '*Could not parse GIT_REPOSITORY or GIT_TAG*' + + # Test 3: Missing GIT_TAG + $testFile3 = "$tempDir/missing-tag.cmake" + @' +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native +) +'@ | Out-File $testFile3 + + { Parse-CMakeFetchContent $testFile3 'sentry-native' } | Should -Throw '*Could not parse GIT_REPOSITORY or GIT_TAG*' } } Context 'Find-TagForHash' { - It 'returns null for hash without matching tag' { - # Use a fake hash that won't match any real tag + It 'handles hash resolution scenarios' { + # Test 1: Hash without matching tag $fakeHash = 'abcdef1234567890abcdef1234567890abcdef12' $repo = 'https://github.com/getsentry/sentry-native' $result = Find-TagForHash $repo $fakeHash - $result | Should -BeNullOrEmpty - } - It 'handles network failures gracefully' { + # Test 2: Network failures $invalidRepo = 'https://github.com/nonexistent/repo' $hash = 'abcdef1234567890abcdef1234567890abcdef12' # Should not throw, but return null and show warning $result = Find-TagForHash $invalidRepo $hash - $result | Should -BeNullOrEmpty } @@ -109,69 +181,101 @@ Describe 'CMake Helper Functions' { Context 'Update-CMakeFile' { BeforeEach { - # Create a temporary copy of test files for modification $script:tempDir = "$TestDrive/cmake-tests" New-Item $tempDir -ItemType Directory -Force | Out-Null } - It 'updates tag to tag preserving format' { - $sourceFile = "$testDataDir/single-dependency.cmake" - $testFile = "$tempDir/test.cmake" - Copy-Item $sourceFile $testFile + It 'updates CMake files preserving format and structure' { + # Test 1: Tag to tag update + $testFile1 = "$tempDir/tag-update.cmake" + @' +include(FetchContent) - Update-CMakeFile $testFile 'sentry-native' 'v0.9.2' +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG v0.9.1 + GIT_SHALLOW FALSE +) - $content = Get-Content $testFile -Raw +FetchContent_MakeAvailable(sentry-native) +'@ | Out-File $testFile1 + + Update-CMakeFile $testFile1 'sentry-native' 'v0.9.2' + + $content = Get-Content $testFile1 -Raw $content | Should -Match 'GIT_TAG v0.9.2' $content | Should -Not -Match 'v0.9.1' - } + # Verify structure preservation + $content | Should -Match 'include\(FetchContent\)' + $content | Should -Match 'FetchContent_MakeAvailable' + $content | Should -Match 'GIT_REPOSITORY https://github.com/getsentry/sentry-native' + $content | Should -Match 'GIT_SHALLOW FALSE' + + # Test 2: Hash to newer hash update + $testFile2 = "$tempDir/hash-update.cmake" + @' +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2 # 0.9.1 + GIT_SHALLOW FALSE + GIT_SUBMODULES "external/breakpad" +) - It 'updates hash to newer hash preserving format' { - $sourceFile = "$testDataDir/hash-dependency.cmake" - $testFile = "$tempDir/test.cmake" - Copy-Item $sourceFile $testFile +FetchContent_MakeAvailable(sentry-native) +'@ | Out-File $testFile2 # Update to a newer tag that will be converted to hash (0.11.0 is known to exist) - Update-CMakeFile $testFile 'sentry-native' '0.11.0' + Update-CMakeFile $testFile2 'sentry-native' '0.11.0' - $content = Get-Content $testFile -Raw + $content = Get-Content $testFile2 -Raw # Should have new hash with tag comment $content | Should -Match 'GIT_TAG [a-f0-9]{40} # 0.11.0' # Should not have old hash or old comment $content | Should -Not -Match 'a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2' $content | Should -Not -Match '# 0.9.1' - } - - It 'preserves file structure and other content' { - $sourceFile = "$testDataDir/single-dependency.cmake" - $testFile = "$tempDir/test.cmake" - Copy-Item $sourceFile $testFile - Update-CMakeFile $testFile 'sentry-native' 'v0.9.2' - - $content = Get-Content $testFile -Raw - $content | Should -Match 'include\(FetchContent\)' - $content | Should -Match 'FetchContent_MakeAvailable' - $content | Should -Match 'GIT_REPOSITORY https://github.com/getsentry/sentry-native' - $content | Should -Match 'GIT_SHALLOW FALSE' - } - - It 'handles complex formatting correctly' { - $sourceFile = "$testDataDir/complex-formatting.cmake" - $testFile = "$tempDir/test.cmake" - Copy-Item $sourceFile $testFile - - Update-CMakeFile $testFile 'sentry-native' 'v0.9.2' - - $content = Get-Content $testFile -Raw + # Test 3: Complex formatting + $testFile3 = "$tempDir/complex-format.cmake" + @' +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY + https://github.com/getsentry/sentry-native + GIT_TAG + v0.9.1 + GIT_SHALLOW + FALSE + GIT_SUBMODULES + "external/breakpad" +) + +FetchContent_MakeAvailable(sentry-native) +'@ | Out-File $testFile3 + + Update-CMakeFile $testFile3 'sentry-native' 'v0.9.2' + + $content = Get-Content $testFile3 -Raw $content | Should -Match 'GIT_TAG\s+v0.9.2' $content | Should -Not -Match 'v0.9.1' } - It 'throws on failed regex match' { - $sourceFile = "$testDataDir/single-dependency.cmake" - $testFile = "$tempDir/test.cmake" - Copy-Item $sourceFile $testFile + It 'handles error scenarios appropriately' { + $testFile = "$tempDir/error-test.cmake" + @' +include(FetchContent) + +FetchContent_Declare( + sentry-native + GIT_REPOSITORY https://github.com/getsentry/sentry-native + GIT_TAG v0.9.1 +) +'@ | Out-File $testFile # Try to update a dependency that doesn't exist { Update-CMakeFile $testFile 'nonexistent-dep' 'v1.0.0' } | Should -Throw "*FetchContent_Declare for 'nonexistent-dep' not found*" @@ -180,4 +284,4 @@ Describe 'CMake Helper Functions' { # Note: Hash update tests require network access for git ls-remote # and are better suited for integration tests } -} +} \ No newline at end of file From b419603d7f734aa4c3db4ac2a5c4bb23e2ed81c6 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 19 Sep 2025 08:54:23 +0200 Subject: [PATCH 13/21] refactor: Improve test structure with shared data and individual cases - Move test data creation to Context BeforeAll level - Restore individual test cases (16 total) for focused testing - Eliminate data duplication while keeping inline visibility - Best of both worlds: shared setup + granular test cases Structure: - Context BeforeAll: Creates shared test files with inline data - Individual It blocks: Reference shared files for specific scenarios - Clear test names and focused assertions per test case --- .../tests/update-dependency-cmake.Tests.ps1 | 259 +++++++++--------- 1 file changed, 136 insertions(+), 123 deletions(-) diff --git a/updater/tests/update-dependency-cmake.Tests.ps1 b/updater/tests/update-dependency-cmake.Tests.ps1 index c4a04b70..3a6e605d 100644 --- a/updater/tests/update-dependency-cmake.Tests.ps1 +++ b/updater/tests/update-dependency-cmake.Tests.ps1 @@ -5,14 +5,12 @@ BeforeAll { Describe 'CMake Helper Functions' { Context 'Parse-CMakeFetchContent' { - BeforeEach { + BeforeAll { $script:tempDir = "$TestDrive/cmake-tests" New-Item $tempDir -ItemType Directory -Force | Out-Null - } - It 'parses FetchContent_Declare with various scenarios' { - # Test 1: Basic parsing with explicit dependency name - $testFile1 = "$tempDir/basic.cmake" + # Create test files with inline data + $script:basicFile = "$tempDir/basic.cmake" @' include(FetchContent) @@ -24,21 +22,9 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(sentry-native) -'@ | Out-File $testFile1 +'@ | Out-File $basicFile - $result = Parse-CMakeFetchContent $testFile1 'sentry-native' - $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' - $result.GitTag | Should -Be 'v0.9.1' - $result.DepName | Should -Be 'sentry-native' - - # Test 2: Auto-detection with same file - $result = Parse-CMakeFetchContent $testFile1 $null - $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' - $result.GitTag | Should -Be 'v0.9.1' - $result.DepName | Should -Be 'sentry-native' - - # Test 3: Hash values - $testFile2 = "$tempDir/hash.cmake" + $script:hashFile = "$tempDir/hash.cmake" @' include(FetchContent) @@ -51,15 +37,9 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(sentry-native) -'@ | Out-File $testFile2 - - $result = Parse-CMakeFetchContent $testFile2 'sentry-native' - $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' - $result.GitTag | Should -Be 'a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2' - $result.DepName | Should -Be 'sentry-native' +'@ | Out-File $hashFile - # Test 4: Complex multi-line formatting - $testFile3 = "$tempDir/complex.cmake" + $script:complexFile = "$tempDir/complex.cmake" @' include(FetchContent) @@ -76,16 +56,9 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(sentry-native) -'@ | Out-File $testFile3 +'@ | Out-File $complexFile - $result = Parse-CMakeFetchContent $testFile3 'sentry-native' - $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' - $result.GitTag | Should -Be 'v0.9.1' - $result.DepName | Should -Be 'sentry-native' - } - - It 'handles multiple dependencies correctly' { - $testFile = "$tempDir/multiple.cmake" + $script:multipleFile = "$tempDir/multiple.cmake" @' include(FetchContent) @@ -102,76 +75,104 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(sentry-native googletest) -'@ | Out-File $testFile - - # Should throw when no explicit name given - { Parse-CMakeFetchContent $testFile $null } | Should -Throw '*Multiple FetchContent declarations found*' - - # Should work with explicit dependency name - $result = Parse-CMakeFetchContent $testFile 'googletest' - $result.GitRepository | Should -Be 'https://github.com/google/googletest' - $result.GitTag | Should -Be 'v1.14.0' - $result.DepName | Should -Be 'googletest' - } +'@ | Out-File $multipleFile - It 'throws appropriate errors for invalid scenarios' { - # Test 1: Missing dependency - $testFile1 = "$tempDir/valid.cmake" + $script:missingRepoFile = "$tempDir/missing-repo.cmake" @' include(FetchContent) FetchContent_Declare( sentry-native - GIT_REPOSITORY https://github.com/getsentry/sentry-native GIT_TAG v0.9.1 ) -'@ | Out-File $testFile1 - - { Parse-CMakeFetchContent $testFile1 'nonexistent' } | Should -Throw "*FetchContent_Declare for 'nonexistent' not found*" +'@ | Out-File $missingRepoFile - # Test 2: Missing GIT_REPOSITORY - $testFile2 = "$tempDir/missing-repo.cmake" + $script:missingTagFile = "$tempDir/missing-tag.cmake" @' include(FetchContent) FetchContent_Declare( sentry-native - GIT_TAG v0.9.1 + GIT_REPOSITORY https://github.com/getsentry/sentry-native ) -'@ | Out-File $testFile2 +'@ | Out-File $missingTagFile + } - { Parse-CMakeFetchContent $testFile2 'sentry-native' } | Should -Throw '*Could not parse GIT_REPOSITORY or GIT_TAG*' + It 'parses basic FetchContent_Declare with explicit dependency name' { + $result = Parse-CMakeFetchContent $basicFile 'sentry-native' - # Test 3: Missing GIT_TAG - $testFile3 = "$tempDir/missing-tag.cmake" - @' -include(FetchContent) + $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' + $result.GitTag | Should -Be 'v0.9.1' + $result.DepName | Should -Be 'sentry-native' + } -FetchContent_Declare( - sentry-native - GIT_REPOSITORY https://github.com/getsentry/sentry-native -) -'@ | Out-File $testFile3 + It 'auto-detects single FetchContent_Declare' { + $result = Parse-CMakeFetchContent $basicFile $null - { Parse-CMakeFetchContent $testFile3 'sentry-native' } | Should -Throw '*Could not parse GIT_REPOSITORY or GIT_TAG*' + $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' + $result.GitTag | Should -Be 'v0.9.1' + $result.DepName | Should -Be 'sentry-native' + } + + It 'handles hash values correctly' { + $result = Parse-CMakeFetchContent $hashFile 'sentry-native' + + $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' + $result.GitTag | Should -Be 'a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2' + $result.DepName | Should -Be 'sentry-native' + } + + It 'handles complex multi-line formatting' { + $result = Parse-CMakeFetchContent $complexFile 'sentry-native' + + $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' + $result.GitTag | Should -Be 'v0.9.1' + $result.DepName | Should -Be 'sentry-native' + } + + It 'throws on multiple dependencies without explicit name' { + { Parse-CMakeFetchContent $multipleFile $null } | Should -Throw '*Multiple FetchContent declarations found*' + } + + It 'handles specific dependency from multiple dependencies' { + $result = Parse-CMakeFetchContent $multipleFile 'googletest' + + $result.GitRepository | Should -Be 'https://github.com/google/googletest' + $result.GitTag | Should -Be 'v1.14.0' + $result.DepName | Should -Be 'googletest' + } + + It 'throws on missing dependency' { + { Parse-CMakeFetchContent $basicFile 'nonexistent' } | Should -Throw "*FetchContent_Declare for 'nonexistent' not found*" + } + + It 'throws on missing GIT_REPOSITORY' { + { Parse-CMakeFetchContent $missingRepoFile 'sentry-native' } | Should -Throw '*Could not parse GIT_REPOSITORY or GIT_TAG*' + } + + It 'throws on missing GIT_TAG' { + { Parse-CMakeFetchContent $missingTagFile 'sentry-native' } | Should -Throw '*Could not parse GIT_REPOSITORY or GIT_TAG*' } } Context 'Find-TagForHash' { - It 'handles hash resolution scenarios' { - # Test 1: Hash without matching tag + It 'returns null for hash without matching tag' { + # Use a fake hash that won't match any real tag $fakeHash = 'abcdef1234567890abcdef1234567890abcdef12' $repo = 'https://github.com/getsentry/sentry-native' $result = Find-TagForHash $repo $fakeHash + $result | Should -BeNullOrEmpty + } - # Test 2: Network failures + It 'handles network failures gracefully' { $invalidRepo = 'https://github.com/nonexistent/repo' $hash = 'abcdef1234567890abcdef1234567890abcdef12' # Should not throw, but return null and show warning $result = Find-TagForHash $invalidRepo $hash + $result | Should -BeNullOrEmpty } @@ -180,15 +181,12 @@ FetchContent_Declare( } Context 'Update-CMakeFile' { - BeforeEach { - $script:tempDir = "$TestDrive/cmake-tests" + BeforeAll { + $script:tempDir = "$TestDrive/cmake-update-tests" New-Item $tempDir -ItemType Directory -Force | Out-Null - } - It 'updates CMake files preserving format and structure' { - # Test 1: Tag to tag update - $testFile1 = "$tempDir/tag-update.cmake" - @' + # Template for basic CMake file + $script:basicTemplate = @' include(FetchContent) FetchContent_Declare( @@ -199,22 +197,10 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(sentry-native) -'@ | Out-File $testFile1 - - Update-CMakeFile $testFile1 'sentry-native' 'v0.9.2' +'@ - $content = Get-Content $testFile1 -Raw - $content | Should -Match 'GIT_TAG v0.9.2' - $content | Should -Not -Match 'v0.9.1' - # Verify structure preservation - $content | Should -Match 'include\(FetchContent\)' - $content | Should -Match 'FetchContent_MakeAvailable' - $content | Should -Match 'GIT_REPOSITORY https://github.com/getsentry/sentry-native' - $content | Should -Match 'GIT_SHALLOW FALSE' - - # Test 2: Hash to newer hash update - $testFile2 = "$tempDir/hash-update.cmake" - @' + # Template for hash-based CMake file + $script:hashTemplate = @' include(FetchContent) FetchContent_Declare( @@ -226,21 +212,10 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(sentry-native) -'@ | Out-File $testFile2 - - # Update to a newer tag that will be converted to hash (0.11.0 is known to exist) - Update-CMakeFile $testFile2 'sentry-native' '0.11.0' - - $content = Get-Content $testFile2 -Raw - # Should have new hash with tag comment - $content | Should -Match 'GIT_TAG [a-f0-9]{40} # 0.11.0' - # Should not have old hash or old comment - $content | Should -Not -Match 'a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2' - $content | Should -Not -Match '# 0.9.1' +'@ - # Test 3: Complex formatting - $testFile3 = "$tempDir/complex-format.cmake" - @' + # Template for complex formatting + $script:complexTemplate = @' include(FetchContent) FetchContent_Declare( @@ -256,29 +231,67 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(sentry-native) -'@ | Out-File $testFile3 +'@ + } - Update-CMakeFile $testFile3 'sentry-native' 'v0.9.2' + BeforeEach { + # Create fresh test files for each test + $script:basicTestFile = "$tempDir/basic-test.cmake" + $script:hashTestFile = "$tempDir/hash-test.cmake" + $script:complexTestFile = "$tempDir/complex-test.cmake" + } - $content = Get-Content $testFile3 -Raw - $content | Should -Match 'GIT_TAG\s+v0.9.2' + It 'updates tag to tag preserving format' { + $basicTemplate | Out-File $basicTestFile + + Update-CMakeFile $basicTestFile 'sentry-native' 'v0.9.2' + + $content = Get-Content $basicTestFile -Raw + $content | Should -Match 'GIT_TAG v0.9.2' $content | Should -Not -Match 'v0.9.1' } - It 'handles error scenarios appropriately' { - $testFile = "$tempDir/error-test.cmake" - @' -include(FetchContent) + It 'updates hash to newer hash preserving format' { + $hashTemplate | Out-File $hashTestFile -FetchContent_Declare( - sentry-native - GIT_REPOSITORY https://github.com/getsentry/sentry-native - GIT_TAG v0.9.1 -) -'@ | Out-File $testFile + # Update to a newer tag that will be converted to hash (0.11.0 is known to exist) + Update-CMakeFile $hashTestFile 'sentry-native' '0.11.0' + + $content = Get-Content $hashTestFile -Raw + # Should have new hash with tag comment + $content | Should -Match 'GIT_TAG [a-f0-9]{40} # 0.11.0' + # Should not have old hash or old comment + $content | Should -Not -Match 'a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2' + $content | Should -Not -Match '# 0.9.1' + } + + It 'preserves file structure and other content' { + $basicTemplate | Out-File $basicTestFile + + Update-CMakeFile $basicTestFile 'sentry-native' 'v0.9.2' + + $content = Get-Content $basicTestFile -Raw + $content | Should -Match 'include\(FetchContent\)' + $content | Should -Match 'FetchContent_MakeAvailable' + $content | Should -Match 'GIT_REPOSITORY https://github.com/getsentry/sentry-native' + $content | Should -Match 'GIT_SHALLOW FALSE' + } + + It 'handles complex formatting correctly' { + $complexTemplate | Out-File $complexTestFile + + Update-CMakeFile $complexTestFile 'sentry-native' 'v0.9.2' + + $content = Get-Content $complexTestFile -Raw + $content | Should -Match 'GIT_TAG\s+v0.9.2' + $content | Should -Not -Match 'v0.9.1' + } + + It 'throws on failed regex match' { + $basicTemplate | Out-File $basicTestFile # Try to update a dependency that doesn't exist - { Update-CMakeFile $testFile 'nonexistent-dep' 'v1.0.0' } | Should -Throw "*FetchContent_Declare for 'nonexistent-dep' not found*" + { Update-CMakeFile $basicTestFile 'nonexistent-dep' 'v1.0.0' } | Should -Throw "*FetchContent_Declare for 'nonexistent-dep' not found*" } # Note: Hash update tests require network access for git ls-remote From 6888e3f127bd8912fceb876bc3bf2c4d23c0cae7 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 19 Sep 2025 09:14:56 +0200 Subject: [PATCH 14/21] refactor: Reorganize test hierarchy for better clarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Promote function names to Describe level (Parse-CMakeFetchContent, Find-TagForHash, Update-CMakeFile) - Group tests by CMake file type at Context level - Each Context has its own test data (no duplication) - Clear logical organization: function -> file type -> specific tests Structure: ├── Describe 'Parse-CMakeFetchContent' │ ├── Context 'Basic single dependency file' (3 tests) │ ├── Context 'Hash-based dependency file' (1 test) │ ├── Context 'Complex formatting file' (1 test) │ ├── Context 'Multiple dependencies file' (2 tests) │ └── Context 'Malformed files' (2 tests) ├── Describe 'Find-TagForHash' │ └── Context 'Hash resolution scenarios' (2 tests) └── Describe 'Update-CMakeFile' ├── Context 'Basic tag updates' (3 tests) ├── Context 'Hash updates' (1 test) └── Context 'Complex formatting' (1 test) --- .../tests/update-dependency-cmake.Tests.ps1 | 257 +++++++++++------- 1 file changed, 152 insertions(+), 105 deletions(-) diff --git a/updater/tests/update-dependency-cmake.Tests.ps1 b/updater/tests/update-dependency-cmake.Tests.ps1 index 3a6e605d..c74da88b 100644 --- a/updater/tests/update-dependency-cmake.Tests.ps1 +++ b/updater/tests/update-dependency-cmake.Tests.ps1 @@ -3,13 +3,12 @@ BeforeAll { . "$PSScriptRoot/../scripts/cmake-functions.ps1" } -Describe 'CMake Helper Functions' { - Context 'Parse-CMakeFetchContent' { +Describe 'Parse-CMakeFetchContent' { + Context 'Basic single dependency file' { BeforeAll { $script:tempDir = "$TestDrive/cmake-tests" New-Item $tempDir -ItemType Directory -Force | Out-Null - # Create test files with inline data $script:basicFile = "$tempDir/basic.cmake" @' include(FetchContent) @@ -23,6 +22,33 @@ FetchContent_Declare( FetchContent_MakeAvailable(sentry-native) '@ | Out-File $basicFile + } + + It 'parses with explicit dependency name' { + $result = Parse-CMakeFetchContent $basicFile 'sentry-native' + + $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' + $result.GitTag | Should -Be 'v0.9.1' + $result.DepName | Should -Be 'sentry-native' + } + + It 'auto-detects single dependency' { + $result = Parse-CMakeFetchContent $basicFile $null + + $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' + $result.GitTag | Should -Be 'v0.9.1' + $result.DepName | Should -Be 'sentry-native' + } + + It 'throws on missing dependency' { + { Parse-CMakeFetchContent $basicFile 'nonexistent' } | Should -Throw "*FetchContent_Declare for 'nonexistent' not found*" + } + } + + Context 'Hash-based dependency file' { + BeforeAll { + $script:tempDir = "$TestDrive/cmake-tests" + New-Item $tempDir -ItemType Directory -Force | Out-Null $script:hashFile = "$tempDir/hash.cmake" @' @@ -38,6 +64,21 @@ FetchContent_Declare( FetchContent_MakeAvailable(sentry-native) '@ | Out-File $hashFile + } + + It 'handles hash values correctly' { + $result = Parse-CMakeFetchContent $hashFile 'sentry-native' + + $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' + $result.GitTag | Should -Be 'a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2' + $result.DepName | Should -Be 'sentry-native' + } + } + + Context 'Complex formatting file' { + BeforeAll { + $script:tempDir = "$TestDrive/cmake-tests" + New-Item $tempDir -ItemType Directory -Force | Out-Null $script:complexFile = "$tempDir/complex.cmake" @' @@ -57,6 +98,21 @@ FetchContent_Declare( FetchContent_MakeAvailable(sentry-native) '@ | Out-File $complexFile + } + + It 'handles complex multi-line formatting' { + $result = Parse-CMakeFetchContent $complexFile 'sentry-native' + + $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' + $result.GitTag | Should -Be 'v0.9.1' + $result.DepName | Should -Be 'sentry-native' + } + } + + Context 'Multiple dependencies file' { + BeforeAll { + $script:tempDir = "$TestDrive/cmake-tests" + New-Item $tempDir -ItemType Directory -Force | Out-Null $script:multipleFile = "$tempDir/multiple.cmake" @' @@ -76,6 +132,25 @@ FetchContent_Declare( FetchContent_MakeAvailable(sentry-native googletest) '@ | Out-File $multipleFile + } + + It 'throws on multiple dependencies without explicit name' { + { Parse-CMakeFetchContent $multipleFile $null } | Should -Throw '*Multiple FetchContent declarations found*' + } + + It 'handles specific dependency from multiple dependencies' { + $result = Parse-CMakeFetchContent $multipleFile 'googletest' + + $result.GitRepository | Should -Be 'https://github.com/google/googletest' + $result.GitTag | Should -Be 'v1.14.0' + $result.DepName | Should -Be 'googletest' + } + } + + Context 'Malformed files' { + BeforeAll { + $script:tempDir = "$TestDrive/cmake-tests" + New-Item $tempDir -ItemType Directory -Force | Out-Null $script:missingRepoFile = "$tempDir/missing-repo.cmake" @' @@ -98,54 +173,6 @@ FetchContent_Declare( '@ | Out-File $missingTagFile } - It 'parses basic FetchContent_Declare with explicit dependency name' { - $result = Parse-CMakeFetchContent $basicFile 'sentry-native' - - $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' - $result.GitTag | Should -Be 'v0.9.1' - $result.DepName | Should -Be 'sentry-native' - } - - It 'auto-detects single FetchContent_Declare' { - $result = Parse-CMakeFetchContent $basicFile $null - - $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' - $result.GitTag | Should -Be 'v0.9.1' - $result.DepName | Should -Be 'sentry-native' - } - - It 'handles hash values correctly' { - $result = Parse-CMakeFetchContent $hashFile 'sentry-native' - - $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' - $result.GitTag | Should -Be 'a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2' - $result.DepName | Should -Be 'sentry-native' - } - - It 'handles complex multi-line formatting' { - $result = Parse-CMakeFetchContent $complexFile 'sentry-native' - - $result.GitRepository | Should -Be 'https://github.com/getsentry/sentry-native' - $result.GitTag | Should -Be 'v0.9.1' - $result.DepName | Should -Be 'sentry-native' - } - - It 'throws on multiple dependencies without explicit name' { - { Parse-CMakeFetchContent $multipleFile $null } | Should -Throw '*Multiple FetchContent declarations found*' - } - - It 'handles specific dependency from multiple dependencies' { - $result = Parse-CMakeFetchContent $multipleFile 'googletest' - - $result.GitRepository | Should -Be 'https://github.com/google/googletest' - $result.GitTag | Should -Be 'v1.14.0' - $result.DepName | Should -Be 'googletest' - } - - It 'throws on missing dependency' { - { Parse-CMakeFetchContent $basicFile 'nonexistent' } | Should -Throw "*FetchContent_Declare for 'nonexistent' not found*" - } - It 'throws on missing GIT_REPOSITORY' { { Parse-CMakeFetchContent $missingRepoFile 'sentry-native' } | Should -Throw '*Could not parse GIT_REPOSITORY or GIT_TAG*' } @@ -154,8 +181,10 @@ FetchContent_Declare( { Parse-CMakeFetchContent $missingTagFile 'sentry-native' } | Should -Throw '*Could not parse GIT_REPOSITORY or GIT_TAG*' } } +} - Context 'Find-TagForHash' { +Describe 'Find-TagForHash' { + Context 'Hash resolution scenarios' { It 'returns null for hash without matching tag' { # Use a fake hash that won't match any real tag $fakeHash = 'abcdef1234567890abcdef1234567890abcdef12' @@ -179,13 +208,14 @@ FetchContent_Declare( # Note: Testing actual hash resolution requires network access # and is better suited for integration tests } +} - Context 'Update-CMakeFile' { +Describe 'Update-CMakeFile' { + Context 'Basic tag updates' { BeforeAll { $script:tempDir = "$TestDrive/cmake-update-tests" New-Item $tempDir -ItemType Directory -Force | Out-Null - # Template for basic CMake file $script:basicTemplate = @' include(FetchContent) @@ -198,8 +228,47 @@ FetchContent_Declare( FetchContent_MakeAvailable(sentry-native) '@ + } + + BeforeEach { + $script:basicTestFile = "$tempDir/basic-test.cmake" + } + + It 'updates tag to tag preserving format' { + $basicTemplate | Out-File $basicTestFile + + Update-CMakeFile $basicTestFile 'sentry-native' 'v0.9.2' + + $content = Get-Content $basicTestFile -Raw + $content | Should -Match 'GIT_TAG v0.9.2' + $content | Should -Not -Match 'v0.9.1' + } + + It 'preserves file structure and other content' { + $basicTemplate | Out-File $basicTestFile + + Update-CMakeFile $basicTestFile 'sentry-native' 'v0.9.2' + + $content = Get-Content $basicTestFile -Raw + $content | Should -Match 'include\(FetchContent\)' + $content | Should -Match 'FetchContent_MakeAvailable' + $content | Should -Match 'GIT_REPOSITORY https://github.com/getsentry/sentry-native' + $content | Should -Match 'GIT_SHALLOW FALSE' + } + + It 'throws on failed regex match' { + $basicTemplate | Out-File $basicTestFile + + # Try to update a dependency that doesn't exist + { Update-CMakeFile $basicTestFile 'nonexistent-dep' 'v1.0.0' } | Should -Throw "*FetchContent_Declare for 'nonexistent-dep' not found*" + } + } + + Context 'Hash updates' { + BeforeAll { + $script:tempDir = "$TestDrive/cmake-update-tests" + New-Item $tempDir -ItemType Directory -Force | Out-Null - # Template for hash-based CMake file $script:hashTemplate = @' include(FetchContent) @@ -211,44 +280,12 @@ FetchContent_Declare( GIT_SUBMODULES "external/breakpad" ) -FetchContent_MakeAvailable(sentry-native) -'@ - - # Template for complex formatting - $script:complexTemplate = @' -include(FetchContent) - -FetchContent_Declare( - sentry-native - GIT_REPOSITORY - https://github.com/getsentry/sentry-native - GIT_TAG - v0.9.1 - GIT_SHALLOW - FALSE - GIT_SUBMODULES - "external/breakpad" -) - FetchContent_MakeAvailable(sentry-native) '@ } BeforeEach { - # Create fresh test files for each test - $script:basicTestFile = "$tempDir/basic-test.cmake" $script:hashTestFile = "$tempDir/hash-test.cmake" - $script:complexTestFile = "$tempDir/complex-test.cmake" - } - - It 'updates tag to tag preserving format' { - $basicTemplate | Out-File $basicTestFile - - Update-CMakeFile $basicTestFile 'sentry-native' 'v0.9.2' - - $content = Get-Content $basicTestFile -Raw - $content | Should -Match 'GIT_TAG v0.9.2' - $content | Should -Not -Match 'v0.9.1' } It 'updates hash to newer hash preserving format' { @@ -264,17 +301,34 @@ FetchContent_MakeAvailable(sentry-native) $content | Should -Not -Match 'a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2' $content | Should -Not -Match '# 0.9.1' } + } - It 'preserves file structure and other content' { - $basicTemplate | Out-File $basicTestFile + Context 'Complex formatting' { + BeforeAll { + $script:tempDir = "$TestDrive/cmake-update-tests" + New-Item $tempDir -ItemType Directory -Force | Out-Null - Update-CMakeFile $basicTestFile 'sentry-native' 'v0.9.2' + $script:complexTemplate = @' +include(FetchContent) - $content = Get-Content $basicTestFile -Raw - $content | Should -Match 'include\(FetchContent\)' - $content | Should -Match 'FetchContent_MakeAvailable' - $content | Should -Match 'GIT_REPOSITORY https://github.com/getsentry/sentry-native' - $content | Should -Match 'GIT_SHALLOW FALSE' +FetchContent_Declare( + sentry-native + GIT_REPOSITORY + https://github.com/getsentry/sentry-native + GIT_TAG + v0.9.1 + GIT_SHALLOW + FALSE + GIT_SUBMODULES + "external/breakpad" +) + +FetchContent_MakeAvailable(sentry-native) +'@ + } + + BeforeEach { + $script:complexTestFile = "$tempDir/complex-test.cmake" } It 'handles complex formatting correctly' { @@ -286,15 +340,8 @@ FetchContent_MakeAvailable(sentry-native) $content | Should -Match 'GIT_TAG\s+v0.9.2' $content | Should -Not -Match 'v0.9.1' } - - It 'throws on failed regex match' { - $basicTemplate | Out-File $basicTestFile - - # Try to update a dependency that doesn't exist - { Update-CMakeFile $basicTestFile 'nonexistent-dep' 'v1.0.0' } | Should -Throw "*FetchContent_Declare for 'nonexistent-dep' not found*" - } - - # Note: Hash update tests require network access for git ls-remote - # and are better suited for integration tests } + + # Note: Hash update tests require network access for git ls-remote + # and are better suited for integration tests } \ No newline at end of file From 814a5c594b35c0cb729bb4f863707fe1d74239c2 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 19 Sep 2025 09:36:58 +0200 Subject: [PATCH 15/21] test: Use exact hash instead of regex pattern in assertion - Replace generic pattern [a-f0-9]{40} with actual 0.11.0 hash - More precise assertion: 3bd091313ae97be90be62696a2babe591a988eb8 - Consistent with integration test data expectations - Eliminates ambiguity in test validation --- updater/tests/update-dependency-cmake.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/updater/tests/update-dependency-cmake.Tests.ps1 b/updater/tests/update-dependency-cmake.Tests.ps1 index c74da88b..cb9343d4 100644 --- a/updater/tests/update-dependency-cmake.Tests.ps1 +++ b/updater/tests/update-dependency-cmake.Tests.ps1 @@ -296,7 +296,7 @@ FetchContent_MakeAvailable(sentry-native) $content = Get-Content $hashTestFile -Raw # Should have new hash with tag comment - $content | Should -Match 'GIT_TAG [a-f0-9]{40} # 0.11.0' + $content | Should -Match 'GIT_TAG 3bd091313ae97be90be62696a2babe591a988eb8 # 0.11.0' # Should not have old hash or old comment $content | Should -Not -Match 'a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2' $content | Should -Not -Match '# 0.9.1' @@ -344,4 +344,4 @@ FetchContent_MakeAvailable(sentry-native) # Note: Hash update tests require network access for git ls-remote # and are better suited for integration tests -} \ No newline at end of file +} From 6b48177fb83a902deff999ccd09885719541c1d6 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 19 Sep 2025 09:46:29 +0200 Subject: [PATCH 16/21] test: Use exact hash in integration test assertion - Replace generic pattern [a-f0-9]{40} # \d+\.\d+\.\d+ with exact values - More precise assertion: 3bd091313ae97be90be62696a2babe591a988eb8 # 0\.11\.0 - Matches unit test precision and validates exact expected output - Eliminates ambiguity in hash-to-tag update validation --- updater/tests/update-dependency.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/updater/tests/update-dependency.Tests.ps1 b/updater/tests/update-dependency.Tests.ps1 index 9d4fecb5..4712b488 100644 --- a/updater/tests/update-dependency.Tests.ps1 +++ b/updater/tests/update-dependency.Tests.ps1 @@ -319,8 +319,8 @@ FetchContent_MakeAvailable(sentry-native) UpdateDependency $testFile $content = Get-Content $testFile -Raw - # Should update to a new hash with tag comment (note: may have comment formatting issues) - $content | Should -Match 'GIT_TAG [a-f0-9]{40} # \d+\.\d+\.\d+' + # Should update to a new hash with tag comment + $content | Should -Match 'GIT_TAG 3bd091313ae97be90be62696a2babe591a988eb8 # 0\.11\.0' $content | Should -Not -Match 'a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2' } From d7e850f936ebe09f9ef4ab5d519d5a23fa7327bf Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 19 Sep 2025 09:47:51 +0200 Subject: [PATCH 17/21] test: Use exact version in remaining integration test assertions - Replace generic \d+\.\d+\.\d+ patterns with exact 0\.11\.0 - More precise assertions for explicit dependency and auto-detection tests - Completes migration from generic patterns to exact expected values - Ensures deterministic test validation across all CMake tests --- updater/tests/update-dependency.Tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/updater/tests/update-dependency.Tests.ps1 b/updater/tests/update-dependency.Tests.ps1 index 4712b488..4358c550 100644 --- a/updater/tests/update-dependency.Tests.ps1 +++ b/updater/tests/update-dependency.Tests.ps1 @@ -275,7 +275,7 @@ FetchContent_MakeAvailable(sentry-native) $content = Get-Content $testFile -Raw $content | Should -Not -Match 'v0.9.1' - $content | Should -Match 'GIT_TAG \d+\.\d+\.\d+' + $content | Should -Match 'GIT_TAG 0\.11\.0' $content | Should -Match 'GIT_REPOSITORY https://github.com/getsentry/sentry-native' } @@ -298,7 +298,7 @@ FetchContent_MakeAvailable(sentry-native) $content = Get-Content $testFile -Raw $content | Should -Not -Match 'v0.9.0' - $content | Should -Match 'GIT_TAG \d+\.\d+\.\d+' + $content | Should -Match 'GIT_TAG 0\.11\.0' } It 'updates from hash to newer tag preserving hash format' { From 132845a2efbc7a42fc7fbf10b5ebb91a39e277bb Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 19 Sep 2025 09:49:27 +0200 Subject: [PATCH 18/21] revert: Use generic patterns in integration tests without version constraints MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Revert exact version assertions where UpdateDependency gets latest version - Keep generic patterns \d+\.\d+\.\d+ and [a-f0-9]{40} for future-proof tests - Integration tests call UpdateDependency without pattern constraints - Latest version will change over time (0.11.0 → 0.12.0, etc.) - Unit tests can keep exact values since they specify exact versions --- updater/tests/update-dependency.Tests.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/updater/tests/update-dependency.Tests.ps1 b/updater/tests/update-dependency.Tests.ps1 index 4358c550..23c5c10b 100644 --- a/updater/tests/update-dependency.Tests.ps1 +++ b/updater/tests/update-dependency.Tests.ps1 @@ -275,7 +275,7 @@ FetchContent_MakeAvailable(sentry-native) $content = Get-Content $testFile -Raw $content | Should -Not -Match 'v0.9.1' - $content | Should -Match 'GIT_TAG 0\.11\.0' + $content | Should -Match 'GIT_TAG \d+\.\d+\.\d+' $content | Should -Match 'GIT_REPOSITORY https://github.com/getsentry/sentry-native' } @@ -298,7 +298,7 @@ FetchContent_MakeAvailable(sentry-native) $content = Get-Content $testFile -Raw $content | Should -Not -Match 'v0.9.0' - $content | Should -Match 'GIT_TAG 0\.11\.0' + $content | Should -Match 'GIT_TAG \d+\.\d+\.\d+' } It 'updates from hash to newer tag preserving hash format' { @@ -320,7 +320,7 @@ FetchContent_MakeAvailable(sentry-native) $content = Get-Content $testFile -Raw # Should update to a new hash with tag comment - $content | Should -Match 'GIT_TAG 3bd091313ae97be90be62696a2babe591a988eb8 # 0\.11\.0' + $content | Should -Match 'GIT_TAG [a-f0-9]{40} # \d+\.\d+\.\d+' $content | Should -Not -Match 'a64d5bd8ee130f2cda196b6fa7d9b65bfa6d32e2' } From cd95e70c222182f230abe6845b43a1bda1e97dd3 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 19 Sep 2025 10:01:53 +0200 Subject: [PATCH 19/21] docs: Add changelog entry for CMake FetchContent support - Document new CMake FetchContent functionality in CHANGELOG.md - References PR #104 for automated dependency updates - Follows existing changelog format and conventions --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ad3522a..b53d035e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Features - Add Proguard artifact endpoint for Android builds in sentry-server ([#100](https://github.com/getsentry/github-workflows/pull/100)) +- Updater - Add CMake FetchContent support for automated dependency updates ([#104](https://github.com/getsentry/github-workflows/pull/104)) ### Security From df907ad80e3c66e49fc591f24ada9c9b8e6dea55 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 19 Sep 2025 11:01:14 +0200 Subject: [PATCH 20/21] Add parameter validation to CMake helper functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added robust parameter validation with type constraints to all CMake helper functions: - Parse-CMakeFetchContent: Validates file path exists and dependency name format - Find-TagForHash: Validates repository URL and 40-char hash format - Test-HashAncestry: Validates repository URL and hash formats - Update-CMakeFile: Validates file path, dependency name, and new value This prevents misuse, improves error handling, and addresses security concerns around parameter injection attacks. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- updater/scripts/cmake-functions.ps1 | 56 ++++++++++++++++++++++++++--- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/updater/scripts/cmake-functions.ps1 b/updater/scripts/cmake-functions.ps1 index 7e7d4e6f..8089e90f 100644 --- a/updater/scripts/cmake-functions.ps1 +++ b/updater/scripts/cmake-functions.ps1 @@ -1,6 +1,16 @@ # CMake FetchContent helper functions for update-dependency.ps1 -function Parse-CMakeFetchContent($filePath, $depName) { +function Parse-CMakeFetchContent { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)] + [ValidateScript({Test-Path $_ -PathType Leaf})] + [string]$filePath, + + [Parameter(Mandatory=$false)] + [ValidateScript({[string]::IsNullOrEmpty($_) -or $_ -match '^[a-zA-Z][a-zA-Z0-9_.-]*$'})] + [string]$depName + ) $content = Get-Content $filePath -Raw if ($depName) { @@ -37,7 +47,17 @@ function Parse-CMakeFetchContent($filePath, $depName) { return @{ GitRepository = $repo; GitTag = $tag; DepName = $depName } } -function Find-TagForHash($repo, $hash) { +function Find-TagForHash { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string]$repo, + + [Parameter(Mandatory=$true)] + [ValidatePattern('^[a-f0-9]{40}$')] + [string]$hash + ) try { $refs = git ls-remote --tags $repo if ($LASTEXITCODE -ne 0) { @@ -57,7 +77,21 @@ function Find-TagForHash($repo, $hash) { } } -function Test-HashAncestry($repo, $oldHash, $newHash) { +function Test-HashAncestry { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string]$repo, + + [Parameter(Mandatory=$true)] + [ValidatePattern('^[a-f0-9]{40}$')] + [string]$oldHash, + + [Parameter(Mandatory=$true)] + [ValidatePattern('^[a-f0-9]{40}$')] + [string]$newHash + ) try { # Create a temporary directory for git operations $tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid()) @@ -92,7 +126,21 @@ function Test-HashAncestry($repo, $oldHash, $newHash) { } } -function Update-CMakeFile($filePath, $depName, $newValue) { +function Update-CMakeFile { + [CmdletBinding()] + param( + [Parameter(Mandatory=$true)] + [ValidateScript({Test-Path $_ -PathType Leaf})] + [string]$filePath, + + [Parameter(Mandatory=$false)] + [ValidateScript({[string]::IsNullOrEmpty($_) -or $_ -match '^[a-zA-Z][a-zA-Z0-9_.-]*$'})] + [string]$depName, + + [Parameter(Mandatory=$true)] + [ValidateNotNullOrEmpty()] + [string]$newValue + ) $content = Get-Content $filePath -Raw $fetchContent = Parse-CMakeFetchContent $filePath $depName $originalValue = $fetchContent.GitTag From 58cef75470ff7075d3241c9430c69a465ac280c7 Mon Sep 17 00:00:00 2001 From: Ivan Dlugos Date: Fri, 19 Sep 2025 11:19:31 +0200 Subject: [PATCH 21/21] Add dependency name validation in update-dependency.ps1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added validation to ensure CMake dependency names follow proper naming conventions and prevent potential regex injection attacks. Dependency names must start with a letter and contain only alphanumeric characters, underscores, dots, or hyphens. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- updater/scripts/update-dependency.ps1 | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/updater/scripts/update-dependency.ps1 b/updater/scripts/update-dependency.ps1 index 6b3f4379..89c9fc18 100644 --- a/updater/scripts/update-dependency.ps1 +++ b/updater/scripts/update-dependency.ps1 @@ -20,10 +20,14 @@ Set-StrictMode -Version latest . "$PSScriptRoot/common.ps1" # Parse CMake file with dependency name -if ($Path -match '^(.+\.cmake)(#.+)?$') { +if ($Path -match '^(.+\.cmake)(#(.+))?$') { $Path = $Matches[1] # Set Path to file for existing logic - if ($Matches[2]) { - $cmakeDep = $Matches[2].TrimStart('#') + if ($Matches[3]) { + $cmakeDep = $Matches[3] + # Validate dependency name follows CMake naming conventions + if ($cmakeDep -notmatch '^[a-zA-Z][a-zA-Z0-9_.-]*$') { + throw "Invalid CMake dependency name: '$cmakeDep'. Must start with letter and contain only alphanumeric, underscore, dot, or hyphen." + } } else { $cmakeDep = $null # Will auto-detect }