Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@

## [Unreleased]

### Added

- `FileDownload` is now supported on all platforms (`core`, `macos`, `linux`); there was no Windows-only code blocking this (#98).
- `FileDownload` relative `Target` paths are now rooted against `$PWD` before resolution, matching the intuitive expectation of callers (#49).

### Fixed

- `FileDownload` no longer misidentifies a directory-like `Target` (no file extension, or trailing slash) as a full file path when its parent happens to exist; the handler now uses a file-extension heuristic to distinguish file targets from container targets and creates the directory when it does not yet exist (#49).

## [0.4.1] - 2026-06-12

### Added
Expand Down Expand Up @@ -127,7 +136,7 @@
### Fixed

- `Get-Dependency` operator-precedence bug when `DependencyType` is
set inside `PSDependOptions`: parenthesization was incorrect, so the

Check warning on line 139 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Continuous Integration / Run Linters

Unknown word (parenthesization)
global default failed to apply to dependencies without an explicit
type (#131, #173).
- `Find-PSDependLocally` now consults *all* installed versions of a
Expand All @@ -138,7 +147,7 @@
passing it to `Import-Module`, preventing failures when prerelease
segments or `vX.Y.Z` tags appear in the resolved version (#140).
- `Chocolatey` install path now passes `--yes` to `choco install`
(previously the script contained the typo `--yess`), so installs

Check warning on line 150 in CHANGELOG.md

View workflow job for this annotation

GitHub Actions / Continuous Integration / Run Linters

Unknown word (yess) Suggestions: (yes, yeas, yens, yeps, yest)
no longer hang waiting on confirmation, and the surrounding code
was tidied for readability (#174).
- `Git` handler now uses the full `Dependency.Target` path when the
Expand Down
2 changes: 1 addition & 1 deletion PSDepend/PSDependMap.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
FileDownload = @{
Script = 'FileDownload.ps1'
Description = 'Download a file'
Supports = 'windows'
Supports = 'windows', 'core', 'macos', 'linux'
}

FileSystem = @{
Expand Down Expand Up @@ -96,9 +96,9 @@
Supports = 'windows', 'core', 'macos', 'linux'
}

WindowsRSAT = @{

Check warning on line 99 in PSDepend/PSDependMap.psd1

View workflow job for this annotation

GitHub Actions / Continuous Integration / Run Linters

Unknown word (RSAT) Suggestions: (resat, rat, rsa, RSA, csat)
Script = 'WindowsRSAT.ps1'

Check warning on line 100 in PSDepend/PSDependMap.psd1

View workflow job for this annotation

GitHub Actions / Continuous Integration / Run Linters

Unknown word (RSAT) Suggestions: (resat, rat, rsa, RSA, csat)
Description = 'Install a WindowsRSAT PowerShell module using Add-WindowsCapability or Install-WindowsFeature, depending on OS'

Check warning on line 101 in PSDepend/PSDependMap.psd1

View workflow job for this annotation

GitHub Actions / Continuous Integration / Run Linters

Unknown word (RSAT) Suggestions: (resat, rat, rsa, RSA, csat)
Supports = 'windows'
}
}
39 changes: 25 additions & 14 deletions PSDepend/PSDependScripts/FileDownload.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
Install: Install the dependency

.EXAMPLE
sqllite_dll = @{

Check warning on line 22 in PSDepend/PSDependScripts/FileDownload.ps1

View workflow job for this annotation

GitHub Actions / Continuous Integration / Run Linters

Unknown word (sqllite) Suggestions: (sqlite, SQLite, sallie, stylite, sulfite)
DependencyType = 'FileDownload'
Source = 'https://github.com/RamblingCookieMonster/PSSQLite/blob/main/PSSQLite/x64/System.Data.SQLite.dll?raw=true'
Target = 'C:\temp'
Expand All @@ -35,10 +35,10 @@

# Downloads System.Data.SQLite.dll to C:\temp\sqlite.dll
#>
[cmdletbinding()]

Check warning on line 38 in PSDepend/PSDependScripts/FileDownload.ps1

View workflow job for this annotation

GitHub Actions / Continuous Integration / Run Linters

Unknown word (cmdletbinding)
param(
[PSTypeName('PSDepend.Dependency')]
[psobject[]]

Check warning on line 41 in PSDepend/PSDependScripts/FileDownload.ps1

View workflow job for this annotation

GitHub Actions / Continuous Integration / Run Linters

Unknown word (psobject) Suggestions: (isobject, isObject, project, object, subject)
$Dependency,

[ValidateSet('Test', 'Install')]
Expand All @@ -46,7 +46,7 @@
)

function Parse-URLForFile {
[cmdletbinding()]

Check warning on line 49 in PSDepend/PSDependScripts/FileDownload.ps1

View workflow job for this annotation

GitHub Actions / Continuous Integration / Run Linters

Unknown word (cmdletbinding)
param($URL)
# This will need work. Assume leaf is file. If CGI exists in leaf, assume it is after the file
$FileName = $URL.split('/')[-1]
Expand Down Expand Up @@ -76,15 +76,16 @@

# Act on target path....
$ToInstall = $False # Anti pattern

# Normalize relative paths against $PWD so callers don't get burned by cwd-dependent splits
if (-not [IO.Path]::IsPathRooted($Target)) {
$Target = Join-Path $PWD $Target
}
Comment on lines +80 to +83

$TargetParent = Split-Path $Target -Parent
$PathToAdd = $Target
if ( (Test-Path $TargetParent) -and -not (Test-Path $Target)) {
# They gave us a full path, don't parse the file name, use this!
$Path = $Target
$ToInstall = $True
Write-Verbose "Found parent [$TargetParent], not target [$Target], assuming this is target file path"
}
elseif (Test-Path $Target -PathType Leaf) {

if (Test-Path $Target -PathType Leaf) {
# File exists. We should download to temp spot, compare hashes, take action as appropriate.
# For now, skip the file.
Write-Verbose "Skipping existing file [$Target]"
Expand All @@ -93,16 +94,26 @@
}
$PathToAdd = Split-Path $Target -Parent
}
elseif (-not (Test-Path $Target)) {
# They gave us something that doesn't look like a new container for a new or existing file. Wat?
Write-Error "Could not find target path [$Target]"
if ($PSDependAction -contains 'Test') {
return $False
elseif ([IO.Path]::GetExtension($Target) -and -not (Test-Path $Target -PathType Container)) {
# Target has a file extension — treat as a full destination file path
if (-not (Test-Path $TargetParent)) {
Write-Error "Could not find parent path [$TargetParent] for target [$Target]"
if ($PSDependAction -contains 'Test') {
return $False
}
}
Comment on lines +99 to +104
else {
$Path = $Target
$ToInstall = $True
Write-Verbose "Target has extension, treating as file path [$Target]"
Comment on lines +106 to +108
}
}
else {
# No extension (or already a container) — treat target as a directory
Write-Verbose "[$Target] is a container, creating path to file"
# We have a target container, now find the name
if (-not (Test-Path $Target)) {
New-Item -ItemType Directory -Path $Target -Force | Out-Null
}
Comment on lines +114 to +116
If ($Name) {
# explicit name
$FileName = $Name
Expand Down Expand Up @@ -138,6 +149,6 @@
}

if ($Dependency.AddToPath) {
Write-Verbose "Setting PATH to`n$($PathToAdd, $env:PATH -join ';' | Out-String)"
Write-Verbose "Setting PATH to`n$($PathToAdd, $env:PATH -join [IO.Path]::PathSeparator | Out-String)"
Add-ToItemCollection -Reference Env:\Path -Item $PathToAdd
}
41 changes: 41 additions & 0 deletions Tests/FileDownload.Type.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,45 @@
}
$result | Should -Be $true
}

It 'Creates a new directory and downloads into it when Target has no extension and does not exist' {
$newDir = Join-Path (New-Item 'TestDrive:/dl5base' -ItemType Directory -Force).FullName 'newcontainer'

Check warning on line 76 in Tests/FileDownload.Type.Tests.ps1

View workflow job for this annotation

GitHub Actions / Continuous Integration / Run Linters

Unknown word (newcontainer) Suggestions: (devcontainer)
$dep = New-PSDependFixture -DependencyName 'https://example.com/sample.dll' -DependencyType 'FileDownload' -Target $newDir
InModuleScope PSDepend -Parameters @{ Dep = $dep; ScriptPath = $script:ScriptPath; T = $newDir } {
& $ScriptPath -Dependency $Dep
}
Should -Invoke -CommandName Get-WebFile -ModuleName PSDepend -Times 1 -Exactly -ParameterFilter {
$URL -eq 'https://example.com/sample.dll' -and ($Path -like "*newcontainer*sample.dll")
}
Test-Path $newDir -PathType Container | Should -Be $true
}

It 'Treats Target as a full file path when it has a file extension and parent exists' {
$targetDir = (New-Item 'TestDrive:/dl6' -ItemType Directory -Force).FullName
$targetFile = Join-Path $targetDir 'out.dll'
$dep = New-PSDependFixture -DependencyName 'https://example.com/other.dll' -DependencyType 'FileDownload' -Target $targetFile
InModuleScope PSDepend -Parameters @{ Dep = $dep; ScriptPath = $script:ScriptPath } {
& $ScriptPath -Dependency $Dep
}
Should -Invoke -CommandName Get-WebFile -ModuleName PSDepend -Times 1 -Exactly -ParameterFilter {
$URL -eq 'https://example.com/other.dll' -and $Path -eq $targetFile
}
}

It 'Roots a relative Target against $PWD and downloads to it' {
$baseDir = (New-Item 'TestDrive:/relbase' -ItemType Directory -Force).FullName
Push-Location $baseDir
try {
$dep = New-PSDependFixture -DependencyName 'https://example.com/sample.dll' -DependencyType 'FileDownload' -Target 'subdir'
InModuleScope PSDepend -Parameters @{ Dep = $dep; ScriptPath = $script:ScriptPath } {
& $ScriptPath -Dependency $Dep
}
Should -Invoke -CommandName Get-WebFile -ModuleName PSDepend -Times 1 -Exactly -ParameterFilter {
$Path -like "*subdir*sample.dll"
}
}
finally {
Pop-Location
}
}
}
Loading