-
-
Notifications
You must be signed in to change notification settings - Fork 8
feat: Add CMake FetchContent support to updater #104
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
0954f89
feat: Add CMake FetchContent support to updater
vaind 891ea75
fix: Complete CMake FetchContent implementation
vaind 9566917
docs: Fix CMake examples to be more logical
vaind ca1444d
docs: Refactor path input description into cleaner sublist
vaind c676be0
fix: cleanup CMake file handling in update-dependency script
vaind e11ac81
fix: ensure newline at end of file in Update-CMakeFile function
vaind 0cea893
refactor: Use cross-platform temp directory approach
vaind e0b5a39
security: Fix ancestry validation to fail safely
vaind b9c8c4f
refactor: Simplify GIT_TAG line replacement logic
vaind 8c0107a
fix: Add proper error handling for git ls-remote commands
vaind f058514
test: Add missing hash-to-hash update test case
vaind 2f572cc
refactor: Inline test data and group related test cases
vaind b419603
refactor: Improve test structure with shared data and individual cases
vaind 6888e3f
refactor: Reorganize test hierarchy for better clarity
vaind 814a5c5
test: Use exact hash instead of regex pattern in assertion
vaind 6b48177
test: Use exact hash in integration test assertion
vaind d7e850f
test: Use exact version in remaining integration test assertions
vaind 132845a
revert: Use generic patterns in integration tests without version con…
vaind cd95e70
docs: Add changelog entry for CMake FetchContent support
vaind df907ad
Add parameter validation to CMake helper functions
vaind 58cef75
Add dependency name validation in update-dependency.ps1
vaind File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,187 @@ | ||
| # CMake FetchContent helper functions for update-dependency.ps1 | ||
|
|
||
| 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) { | ||
| $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') | ||
vaind marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 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+)') | ||
vaind marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| $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 { | ||
| [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) { | ||
| 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) { | ||
| return $tagRef -replace '^refs/tags/', '' | ||
| } | ||
| } | ||
| return $null | ||
| } | ||
| catch { | ||
| Write-Host "Warning: Could not resolve hash $hash to tag name: $_" | ||
| return $null | ||
| } | ||
| } | ||
|
|
||
| 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()) | ||
| 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 "Error: Could not validate ancestry for $oldHash -> $newHash : $_" | ||
| # When in doubt, fail safely to prevent incorrect updates | ||
| return $false | ||
| } | ||
| } | ||
|
|
||
| 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 | ||
| $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 ($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" | ||
| } | ||
| $newHash = ($newHashRefs -split '\s+')[0] | ||
| $replacement = "$newHash # $newValue" | ||
|
|
||
| # Validate ancestry: ensure old hash is reachable from new tag | ||
| 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, 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') | ||
vaind marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| 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" | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.