You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat: add Add-PSDepend command to append dependencies to a DependencyFile - #205
Closes#26 (first opened 2017, revisited now that requirements.lock.json exists — see the design interview on the issue and docs/adr/0001-add-psdepend-file-writer-and-lock-behavior.md).
Add-PSDepend psake latest
# @{ 'psake' = 'latest' }, then re-locks automaticallyAdd-PSDepend-DependencyType GitHub -Name 'RamblingCookieMonster/PowerShell'-Version main
# @{ 'RamblingCookieMonster/PowerShell' = @{ DependencyType = 'GitHub'; Version = 'main' } }
Key decisions (full rationale in the ADR):
No new dependency. The original issue thread suggested PoshCode/Metadata, but Export-Metadata reformats the whole file (drops comments everywhere, not just the new entry) and Update-Metadata can't add new keys at all. Hand-rolled AST splicing only touches the new entry's text.
Lock stays honest.Add-PSDepend re-runs Update-PSDependLock by default (-NoLock to skip) and rolls back the file edit if resolution fails, so the DependencyFile and its lock can't silently drift the way they could before locks existed.
Declare-only. Mirrors the command name; installing is still a separate Invoke-PSDepend call.
Evidence
Before: no Add-PSDepend command; the only way to add a dependency was to hand-edit requirements.psd1.
(./build.ps1 Pester5 — full suite, including 22 new Add-PSDepend tests covering discovery, entry format, DependencyType default resolution, collisions/-Force, comment preservation, lock auto-update/rollback, -WhatIf, and validation. ./build.ps1 Analyze is clean for the new files.)
Smoke-tested end-to-end against the live PowerShell Gallery:
Door: Two-way. New command, zero changes to existing public functions or the DependencyFile schema; nothing else depends on Add-PSDepend yet, so reverting is a plain revert.
Blast Radius: New surface only. Touches no existing code paths — Get-Dependency, Invoke-PSDepend, and Update-PSDependLock are unmodified (only called, not edited). The one behavioral nuance: by default Add-PSDepend makes a network call (via Update-PSDependLock) to resolve the new dependency's version — documented in the README/about-topic and avoidable with -NoLock.
…File
Closes#26.
- Add-PSDepend parses the target DependencyFile with the PowerShell AST
and splices the new entry in as text, leaving every other entry and
any comments byte-for-byte untouched.
- By default it re-runs Update-PSDependLock afterward so the
DependencyFile and its lock never drift apart; -NoLock opts out and
a failed lock step rolls back the file edit.
- Positional shorthand (Add-PSDepend psake latest) plus full named
parameters mirroring the DependencyFile schema. DependencyType
defaults mirror Get-Dependency's own precedence (explicit >
PSDependOptions.DependencyType > GitHub/Git name pattern >
PSGalleryModule).
- Terse 'Name' = 'Version' form when only Name+Version are given and
the effective type is PSGalleryModule (the only type the terse form
round-trips correctly); full hashtable form otherwise.
- Collisions on an existing DependencyName error by default; -Force
fully replaces the entry.
- Declare-only: never installs. Run Invoke-PSDepend separately.
See docs/adr/0001-add-psdepend-file-writer-and-lock-behavior.md for
the design rationale, including why PoshCode/Metadata (suggested in
the original issue thread) was not used.
- Preserve the DependencyFile's original encoding (including BOM) by
reading with StreamReader's BOM auto-detection and writing back with
the same Encoding; new files default to UTF-8 without a BOM. Switch
from Set-Content to [IO.File]::WriteAllText, which throws on failure
instead of Set-Content's non-terminating error, so a failed write can
no longer fall through into the lock step.
- Preserve the file's existing newline style (CRLF/LF) instead of
hardcoding `r`n, and stop TrimEnd()-ing content before the closing
brace, which was silently deleting blank lines and other whitespace
outside the inserted entry.
- Reject an existing non-.psd1 file in Resolve-PSDependFileTarget,
matching the check already applied to new targets; previously an
arbitrary existing file could be edited while Update-PSDependLock
silently skipped it.
- Quote hashtable keys in ConvertTo-PSDependLiteral instead of emitting
them as bareword source, so a -Parameters key with a space or other
non-identifier characters produces valid PowerShell data.
- Serialize an empty array value as '@()' instead of an empty string,
which previously produced invalid syntax like 'Tags = '.
- Correct the .PARAMETER Name help text: entries never emit a separate
Name field, only the DependencyName key.
Adds 9 regression tests covering each of the above.
Addressed all 6 Copilot review findings in 1f14a50 (replied inline on each): original file encoding/BOM is now preserved and writes are terminating so a failed write can't fall through to the lock step, newline style and blank lines before the closing brace are preserved instead of trimmed, Resolve-PSDependFileTarget rejects existing non-.psd1 files, ConvertTo-PSDependLiteral quotes hashtable keys and emits @() for empty arrays, and the .PARAMETER Name help text no longer claims a Name field is emitted. Added 9 regression tests, one per fix. Full suite: 655 passed / 0 failed; Analyze clean.
Despite documenting support for “numbers,” only Int32, Int64, and Double are serialized numerically. Other standard numeric values accepted through -Parameters—such as Decimal, Single, UInt32, or Int16—fall through to the quoted-string branch and silently change type when the file is re-imported. Serialize all supported numeric primitives as valid invariant PowerShell literals, or reject unsupported numeric types instead of converting them to strings.
This issue also appears on line 57 of the same file.
Normalize helper syntax during dependency collision detection
PSDepend/Public/Add-PSDepend.ps1:236
The collision check compares only the raw PSD1 key, so it misses PSDepend's documented helper syntax. For example, an existing 'PSGalleryModule::Pester' = '4.0.0' is parsed by Get-Dependency as DependencyName = 'Pester', but Add-PSDepend Pester ... appends a second logical Pester dependency instead of throwing or replacing it. Normalize helper-form string entries to their suffix during collision detection, retain the original key for the AST replacement, and match that original key at line 310.
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
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.
Summary
Closes #26 (first opened 2017, revisited now that
requirements.lock.jsonexists — see the design interview on the issue anddocs/adr/0001-add-psdepend-file-writer-and-lock-behavior.md).Key decisions (full rationale in the ADR):
PoshCode/Metadata, butExport-Metadatareformats the whole file (drops comments everywhere, not just the new entry) andUpdate-Metadatacan't add new keys at all. Hand-rolled AST splicing only touches the new entry's text.Add-PSDependre-runsUpdate-PSDependLockby default (-NoLockto skip) and rolls back the file edit if resolution fails, so the DependencyFile and its lock can't silently drift the way they could before locks existed.Invoke-PSDependcall.Evidence
Before: no
Add-PSDependcommand; the only way to add a dependency was to hand-editrequirements.psd1.After:
(
./build.ps1 Pester5— full suite, including 22 newAdd-PSDependtests covering discovery, entry format,DependencyTypedefault resolution, collisions/-Force, comment preservation, lock auto-update/rollback,-WhatIf, and validation../build.ps1 Analyzeis clean for the new files.)Smoke-tested end-to-end against the live PowerShell Gallery:
Merge Danger
Door: Two-way. New command, zero changes to existing public functions or the DependencyFile schema; nothing else depends on
Add-PSDependyet, so reverting is a plain revert.Blast Radius: New surface only. Touches no existing code paths —
Get-Dependency,Invoke-PSDepend, andUpdate-PSDependLockare unmodified (only called, not edited). The one behavioral nuance: by defaultAdd-PSDependmakes a network call (viaUpdate-PSDependLock) to resolve the new dependency's version — documented in the README/about-topic and avoidable with-NoLock.