Skip to content
Draft
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
4 changes: 4 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ _Avoid_: filter, category, label
> **Dev:** "I need psake to install before PowerShellBuild. How do I express that?"
> **Domain expert:** "Declare a **Prerequisite** on PowerShellBuild: `DependsOn = 'psake'`. The engine resolves all **Prerequisites** into topological order before dispatching any **DependencyScript**."

**RepositoryRegistry**:
A `Repositories` hashtable nested inside `PSDependOptions` that maps repository names to their source URLs. Used by DependencyScripts to auto-register a repository when it is not already present on the machine.
_Avoid_: repository map, source list, feed declarations

## Flagged ambiguities

- "dependency" collides with **Prerequisite** in natural speech ("psake is a dependency of PowerShellBuild") — resolved: use **Prerequisite** for the ordering relationship, **Dependency** only for a declared entry in a DependencyFile.
Expand Down
25 changes: 23 additions & 2 deletions PSDepend/PSDependScripts/PSGalleryModule.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -167,8 +167,29 @@ Write-Verbose -Message "Getting dependency [$name] from PowerShell repository [$
if ($Repository) {
$validRepo = Get-PSRepository -Name $Repository -Verbose:$false -ErrorAction SilentlyContinue
if (-not $validRepo) {
Write-Error "[$Repository] has not been setup as a valid PowerShell repository."
return
$repoRegistry = $Dependency.PSDependOptions.Repositories
if (-not $repoRegistry -or -not $repoRegistry.ContainsKey($Repository)) {
Write-Error "[$Repository] is not registered and no URL was found in PSDependOptions.Repositories. Add an entry to register it automatically."
return
}
$repoUrl = $repoRegistry[$Repository]
if ($repoUrl -isnot [string]) {
Write-Error "PSDependOptions.Repositories entry for [$Repository] must be a string URL for PSGalleryModule dependencies."
return
}
$registerSplat = @{
Name = $Repository
SourceLocation = $repoUrl
InstallationPolicy = 'Trusted'
}
if ($Credential) { $registerSplat.Credential = $Credential }
Write-Verbose "Registering PowerShell repository [$Repository] at [$repoUrl]"
Register-PSRepository @registerSplat
} elseif ($Dependency.PSDependOptions.Repositories -and $Dependency.PSDependOptions.Repositories.ContainsKey($Repository)) {
$declaredUrl = $Dependency.PSDependOptions.Repositories[$Repository]
if ($declaredUrl -is [string] -and $validRepo.SourceLocation -ne $declaredUrl) {
Write-Warning "Repository [$Repository] is already registered at [$($validRepo.SourceLocation)] but PSDependOptions.Repositories declares [$declaredUrl]. Using existing registration."
}
}
}

Expand Down
25 changes: 23 additions & 2 deletions PSDepend/PSDependScripts/PSResourceGet.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,29 @@ Write-Verbose -Message "Getting dependency [$Name] from PowerShell repository [$
if ($Repository) {
$validRepo = Get-PSResourceRepository -Name $Repository -Verbose:$false -ErrorAction SilentlyContinue
if (-not $validRepo) {
Write-Error "[$Repository] has not been set up as a valid PowerShell repository."
return
$repoRegistry = $Dependency.PSDependOptions.Repositories
if (-not $repoRegistry -or -not $repoRegistry.ContainsKey($Repository)) {
Write-Error "[$Repository] is not registered and no URL was found in PSDependOptions.Repositories. Add an entry to register it automatically."
return
}
$repoUrl = $repoRegistry[$Repository]
if ($repoUrl -isnot [string]) {
Write-Error "PSDependOptions.Repositories entry for [$Repository] must be a string URL for PSResourceGet dependencies."
return
}
$registerSplat = @{
Name = $Repository
Uri = $repoUrl
Trusted = $true
}
if ($Credential) { $registerSplat.Credential = $Credential }
Write-Verbose "Registering PSResource repository [$Repository] at [$repoUrl]"
Register-PSResourceRepository @registerSplat
} elseif ($Dependency.PSDependOptions.Repositories -and $Dependency.PSDependOptions.Repositories.ContainsKey($Repository)) {
$declaredUrl = $Dependency.PSDependOptions.Repositories[$Repository]
if ($declaredUrl -is [string] -and $validRepo.Uri -ne $declaredUrl) {
Write-Warning "Repository [$Repository] is already registered at [$($validRepo.Uri)] but PSDependOptions.Repositories declares [$declaredUrl]. Using existing registration."
}
}
}

Expand Down
28 changes: 26 additions & 2 deletions PSDepend/PSDependScripts/Package.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,32 @@ if (-not $Version) {

$PackageSources = @( Get-PackageSource )
if ($PackageSources.Name -notcontains $Source -and -not $PSBoundParameters.ContainsKey('ProviderName')) {
Write-Error "PackageSource [$Source] is not valid. Valid sources:`n$($PackageSources.ProviderName | Out-String)"
return
$repoRegistry = $Dependency.PSDependOptions.Repositories
if (-not $repoRegistry -or -not $repoRegistry.ContainsKey($Source)) {
Write-Error "PackageSource [$Source] is not registered and no entry was found in PSDependOptions.Repositories. Add an entry with Url and ProviderName to register it automatically."
return
}
$repoEntry = $repoRegistry[$Source]
if ($repoEntry -isnot [hashtable] -or -not $repoEntry.ContainsKey('Url') -or -not $repoEntry.ContainsKey('ProviderName')) {
Write-Error "PSDependOptions.Repositories entry for [$Source] must be a hashtable with 'Url' and 'ProviderName' keys for Package dependencies."
return
}
$registerSplat = @{
Name = $Source
Location = $repoEntry.Url
ProviderName = $repoEntry.ProviderName
Trusted = $true
}
if ($Dependency.Credential) { $registerSplat.Credential = $Dependency.Credential }
Write-Verbose "Registering PackageSource [$Source] at [$($repoEntry.Url)] with provider [$($repoEntry.ProviderName)]"
Register-PackageSource @registerSplat
$PackageSources = @( Get-PackageSource )
} elseif ($repoRegistry -and $repoRegistry.ContainsKey($Source)) {
$repoEntry = $repoRegistry[$Source]
$existingSource = $PackageSources | Where-Object { $_.Name -eq $Source }
if ($existingSource -and $repoEntry -is [hashtable] -and $repoEntry.ContainsKey('Url') -and $existingSource.Location -ne $repoEntry.Url) {
Write-Warning "PackageSource [$Source] is already registered at [$($existingSource.Location)] but PSDependOptions.Repositories declares [$($repoEntry.Url)]. Using existing registration."
}
}

$PackageProviders = @( Get-PackageProvider )
Expand Down
16 changes: 16 additions & 0 deletions docs/adr/0001-repository-registry-in-psdependoptions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Repository auto-registration via PSDependOptions.Repositories

Private repository URLs belong in a file-level `Repositories` block inside `PSDependOptions`, not repeated per-Dependency. When a DependencyScript encounters an unregistered repository name, it looks up that name in `PSDependOptions.Repositories`, registers it persistently as Trusted, and proceeds — making the DependencyFile portable to any machine without manual setup.

## Considered Options

**Per-Dependency `RepositoryUrl` parameter** — the URL travels with each Dependency that uses it. Simpler for single-module cases but forces repetition when multiple Dependencies share a source, and scatters URL maintenance across the file.

**Ephemeral registration** (register before install, unregister after) — leaves no trace on the machine. Rejected because it re-registers on every run when many Dependencies share a source, and teardown on error paths is difficult to make reliable across three different registration APIs.

## Consequences

- `PSDependOptions.Repositories` is a new public key in the DependencyFile schema. Existing files are unaffected (the key is optional).
- Registration is persistent: the repository stays registered after PSDepend runs. This is intentional — machines in CI or shared environments accumulate registrations over time rather than re-registering on each run.
- `Test` action never registers anything; registration is gated on `Install`.
- If a repository name is already registered with a different URL, PSDepend warns but proceeds using the existing registration.
Loading