Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
4f3c4eb
feat: Update documentation and tests for GitHub Actions platform matr…
stiwicourage Oct 23, 2026
1b92f1a
feat: Add GitHub Actions workflows for module publishing, testing, de…
stiwicourage Oct 24, 2026
e945166
feat: Enhance CI workflow with owned dependency management and cleanu…
stiwicourage Oct 24, 2026
68ef516
feat: Update PSScriptAnalyzer settings and tests to allow intentional…
stiwicourage Oct 24, 2026
9798a2a
feat: Update CI workflow policies and tests to enforce platform matri…
stiwicourage Oct 24, 2026
5cc9cd3
fix: Standardize workflow names for consistency across CI/CD files
stiwicourage Oct 24, 2026
4bf0306
fix: Adding KeepAChangelog to Install-CiPowerShellModules.ps1
stiwicourage Oct 24, 2026
1b81301
feat: Enable unit coverage in CI workflow and add coverage report gen…
stiwicourage Oct 24, 2026
bb69f22
fix: Enhance test for named install step to support multiple line end…
stiwicourage Oct 24, 2026
3f0c01e
feat: Expose isolated CI modules to later publish steps and update tests
stiwicourage Oct 24, 2026
809cdc1
feat: Add GitHub Actions for coverage checks, annotated tags, verifie…
stiwicourage Oct 24, 2026
ea8f249
chore: prepare next prerelease version
github-actions[bot] Oct 24, 2026
f9896bb
fix: update coverage percentage target to 95 in project configuration
stiwicourage Oct 24, 2026
8a9288b
fix: ensure final newlines in settings and project files
stiwicourage Oct 24, 2026
84c9769
fix: add various rejection scenarios for module and provider operations
stiwicourage Oct 24, 2026
ec7ae0d
chore: prepare develop for next prerelease
github-actions[bot] Oct 24, 2026
414af13
fix: adjust CI workflow triggers for pull requests and pushes
stiwicourage Oct 24, 2026
cbbbbff
fix: Merge branch 'develop' of https://github.com/stiwicourage/PSModu…
stiwicourage Oct 24, 2026
5213e4f
fix: update CI workflow tests for pull request and push branch handling
stiwicourage Oct 24, 2026
5933d68
Bug/2 fix core update discovery for mixed flat and versioned module i…
stiwicourage Oct 25, 2026
e86b02a
chore: prepare next prerelease version
github-actions[bot] Oct 25, 2026
923dc67
fix: stabilize native module updates and dependency provisioning (#4)
stiwicourage Oct 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
1 change: 1 addition & 0 deletions .github/agents/powershell-developer.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Implement PowerShell command and helper changes in the PSModuleUpdate style.
- Keep one externally called function per file and match the file name to that function. `src/public/*.ps1` files keep exactly one top-level public function; when a public command needs supporting logic, extract it to `src/private/` instead of adding more top-level functions to the public file. In `src/private/`, additional related functions may stay only as same-file top-level support helpers called by that file's entry function, and PowerShell functions must not declare nested functions inside their bodies.
- Use `.github/instructions/code-quality-matrix.instructions.md` as the best-effort source-code maintainability guidance while shaping `src/**/*.ps1`.
- Use `.github/instructions/psscriptanalyzer.instructions.md` as the ScriptAnalyzer workflow source of truth while changing PowerShell code or analyzer helpers.
- Follow the CI parity and readiness gate in `.github/instructions/repository-conventions.instructions.md`: inspect affected workflows before implementation, review test portability and dependency delivery, and distinguish local checks from exact-revision hosted matrix results before handoff.
- Add or update tests that follow the naming convention `tests/public/<Command>.Tests.ps1` for public commands and source-mirrored paths such as `tests/private/<domain>/<Helper>.Tests.ps1` for private helpers, one test file per source file, and keep valid PlatyPS-compatible help docs for the changed behavior using the Microsoft.PowerShell.PlatyPS cmdlets instead of hand-written help structure.
- Every new public entry point must add its matching help file in the same change.
- Before handoff, review every changed or generated text file and normalize it to exactly one trailing newline with no extra blank lines at the bottom.
Expand Down
3 changes: 2 additions & 1 deletion .github/agents/test-engineer.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ Improve or maintain the repository's Pester coverage, coverage-gate behavior, an

## Constraints

- 1. Use `Invoke-NovaTest` when the change affects only in-memory logic with no module-load or file-system dependency. 2. Escalate to `Test-NovaBuild` when the change affects module manifest, exported commands, or build artifacts. 3. Run the full repo quality loop only when step 2 passes and a quality tooling regression is still open.
- Start with focused `Invoke-NovaTest` validation and use `Test-NovaBuild` for built-module/integration behavior. Run the required full repository quality loop for code or test changes regardless of whether a quality tooling regression remains open. Follow the CI parity and readiness gate in `.github/instructions/repository-conventions.instructions.md`; local success does not qualify other platforms or dependency environments.
- Passing tests are not enough if any quality tooling hotspot score for a changed file worsens compared to the baseline on the default branch.
- If an existing fixture or support helper in `tests/*TestSupport.ps1` covers at least 80% of the setup needed, extend it rather than creating a new file. Only create a new support file when no existing helper addresses the scenario.
- Do not group unrelated source files into one broad test file when mirrored `tests/public`, `tests/private`, or `tests/classes` ownership is possible. For source files outside `public`, `private`, and `classes` subdirectories, place tests in a `tests/<matching-subdirectory>/` folder that mirrors the source path. If no matching subdirectory exists, create it rather than placing the test in the root `tests/` folder.
Expand All @@ -55,6 +55,7 @@ Improve or maintain the repository's Pester coverage, coverage-gate behavior, an
- The touched tests are readable and low-duplication.
- Validation uses `Invoke-NovaTest` for unit execution and `Test-NovaBuild` for build-validation integration execution.
- Validation and quality tooling implications are addressed.
- Local and hosted validation evidence is reported separately under the repository CI parity gate; every required matrix lane and aggregate gate must pass for the exact revision before claiming CI or PR readiness.
- The pre-commit quality tooling safeguard is clean before the work is treated as commit-ready when local quality tooling is available. If local quality tooling is unavailable, document the gap in a `# TODO: verify quality tooling` comment on the PR description and do not block the commit, but treat the item as open.

## Must not do
Expand Down
19 changes: 19 additions & 0 deletions .github/instructions/repository-conventions.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,25 @@ If quality tooling is unavailable locally, continue with the normal repository v

If the only files changed are `.md` files and none of those files contain embedded code blocks that are executed as part of a test or build step, executable validation may be skipped.

## CI parity and readiness

- Before changing code or tests, inspect the active `.github/workflows/` definitions and their owning `scripts/build/ci/` helpers. Identify the required OS, architecture, PowerShell versions, dependency provisioning and checks affected by the change; shared tests can break a platform even when production code did not change.
- Run the focused checks and the required local quality loop. Local `run.ps1` success is not matrix qualification. Use the configured lane entrypoint on a matching host/runtime where available, preserving dependency isolation and failure semantics; never spoof runner identity or bypass host guards.
- Review changed tests for portability, including path separators, casing, filesystem ordering and platform-specific commands. Build expected filesystem paths with `Join-Path` and compare the intended path contract, not a string containing the development host's separator. Preserve exact identity and side-effect assertions.
- Before raising a dependency minimum, verify that CI's configured provider and version-selection rules can supply a compatible artifact. Record the actual imported version and path in the test process, not just the launcher's installed-module inventory; a locally built or installed sibling module does not prove availability on a clean runner. An unavailable dependency blocks consumer readiness until an upstream release or explicit CI artifact-delivery change is separately authorized. Do not lower the minimum, add fallback imports, publish packages or modify global module installations to hide the gap.
- Report local and hosted evidence separately: tested revision/content state, OS/architecture, actual PowerShell and dependency versions, exact commands, test/coverage results and relevant run/job URLs. Mark unavailable hosts/runtimes, skipped required tests, interrupted commands and missing artifacts as unverified, not passed.
- Inspect every required hosted job and the aggregate gate for the exact revision and run attempt before claiming CI or PR readiness. An earlier revision's green run, a green quality job or four passing lanes cannot qualify a failing six-lane matrix. Uncommitted fixes remain pending hosted verification; a local handoff must say so.
- Trace failures to the first failing assertion or provisioning step before changing code. Keep regression assertions and thresholds intact; do not skip Windows tests, weaken comparisons, retry until green without diagnosis, or treat downstream missing-phase/artifact errors as the primary defect.

For PSModuleUpdate, `.github/workflows/ci.yml` and `scripts/build/ci/CiLanes.json`
define six required platform/runtime lanes. Reproduce applicable local lanes with
the absolute selected PowerShell executable and
`./scripts/build/ci/Invoke-PlatformLane.ps1 -LaneId <lane-id>` on a matching host.
Preserve its owned dependency root, runtime/test evidence and cleanup; do not
substitute ambient modules or direct Pester. Both Windows lanes need actual
Windows execution; macOS/Linux results cannot establish their success. Check
`ci-gate` and the separate test/coverage workflow for the tested revision.

## Documentation review

- Review `README.md`, `CONTRIBUTING.md`, `CHANGELOG.md`, and `RELEASE_NOTE.md` after any change that adds, removes, or alters public-facing behavior, commands, parameters, or documented workflows.
Expand Down
11 changes: 8 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
name: Test platform matrix

on:
pull_request:
branches: [develop, main]
push:
branches: [develop, main]
branches-ignore:
- main
pull_request:
branches:
- main
- develop
schedule:
- cron: "0 3 * * *"
workflow_call:
workflow_dispatch:

Expand Down
37 changes: 36 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,48 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

### Changed

- Use native PowerShellGet staging and `Update-Module` for existing
installations without Force or an install fallback, while retaining
PSResourceGet discovery, exact target/source validation, consent and recovery.
A worker handoff preserves the original installation lock during mutation.
Operation-owned repository initialization handles defaults populated lazily
during native registration, preventing intermittent pre-installation failure
without changing the user's repositories.
Native updates use the alphanumeric prerelease labels generated by
Gerber/NovaModuleTools; dotted labels are outside this provider's install
capabilities. Historical qualification does not establish untested platform
support.

### Deprecated

### Removed

### Fixed

- Declare Microsoft.PowerShell.PSResourceGet 1.2.0 as PMU's exact runtime
dependency so generated module manifests enforce the qualified provider
baseline before PMU commands run.
- Linux worker ownership checks no longer reject a live approving process
because of inconsistent cross-process timestamps. Exact PID-reuse protection
remains enabled. Legacy Linux repository receipts require explicit recovery
rather than automatic cleanup.
- CI dependency provisioning initializes a missing default PSGallery
registration before downloading and removes only a registration it explicitly
created afterward, preserving existing sources and trust. Package downloads have
five bounded attempts against the same source, destination and pinned
versions; exhausted downloads and isolated-import failures still fail CI
and clean up the owned dependency directory.
- Forward the complete planned target version unchanged at the provider
boundary instead of wrapping it in NuGet range brackets.
Bracketed prerelease targets could be discovered but failed during staging
from PSGallery. Exact target verification, consent and dependency checks
remain enabled.
- Fixed installed-copy discovery failing before repository lookup when a legacy
flat installation leaves resource directories beside numeric version folders.
Ordinary resource folders are excluded, while numeric folders with missing
manifests and manifest-bearing invalid layouts still fail validation. No
installation is migrated or removed.

### Security

## [0.1.0] - 2026-10-24
Expand Down Expand Up @@ -62,4 +98,3 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
- Pass the owned temporary dependency root between `Run Tests` workflow steps
so NovaModuleTools is discoverable for CI and changelog validation, with
ownership-validated cleanup even after a failed consumer step.

17 changes: 17 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Thank you for contributing. Keep changes small, reviewable, and easy to validate.

The unreleased native-update transition is not covered by historical G-4A3
acceptance. Read the [current qualification and native provider scope](docs/architecture/TESTING.md#native-update-transition)
before changing its provider, lifecycle tests, or release requirements.

G-4A3's [accepted closure](docs/architecture/G4A3_CLOSURE.md) is separate from future platform
qualification. Review the proposed [cross-platform strategy](docs/architecture/CROSS_PLATFORM_TESTING.md)
before implementing testing infrastructure; its matrices and roadmap are not
Expand Down Expand Up @@ -36,6 +40,11 @@ Before opening a pull request:

## Agentic Copilot workflow

Follow the [CI parity and readiness gate](.github/instructions/repository-conventions.instructions.md#ci-parity-and-readiness)
for every implementation or test change. A local pass is not a six-lane matrix
pass; report the tested runtime/dependencies and any pending or failing hosted
jobs for the exact revision.

Recommended flow:

1. **Design**
Expand Down Expand Up @@ -63,6 +72,14 @@ PSResourceGet repository state is preserved by the provider fixtures, not by
assuming that `HOME` or `PSModulePath` redirects registrations. Unexpected
skips and incomplete XML/log/runtime evidence fail the lane.

Dependency provisioning checks that PSGallery points to its official endpoint.
If explicit native default registration is needed, it is removed afterward only
if its source and trust remain unchanged; existing registrations are not changed.
Each package download has at most three attempts with the same
source, version selection and owned destination. Exhausted retries or a failed
isolated import probe still fail provisioning and remove its owned dependency
directory.

The separate `Run Tests` workflow also saves dependencies to an owned temporary
root. Each PowerShell consumer step uses the install step's `moduleRoot` output
to set an isolated `PSModulePath`; installed modules and imports do not carry
Expand Down
44 changes: 36 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,22 @@ on PMU or contain any PMU-specific code.

PSModuleUpdate separates read-only discovery from an explicitly approved
installation. It targets PowerShell 7.4+ and uses
`Microsoft.PowerShell.PSResourceGet` 1.2.0 as its qualified provider baseline.
It never persistently configures consumer repositories, stores credentials,
elevates privileges, or reloads the updated module.
`Microsoft.PowerShell.PSResourceGet` 1.2.0 for discovery and declares that exact
version as a module dependency. The unreleased update implementation uses
PowerShellGet 2.2.5 and PackageManagement 1.4.8.1 for staging and native
`Update-Module`, without Force or an `Install-Module` fallback. It never
persistently configures consumer repositories, stores credentials, elevates
privileges, or reloads the updated module.

**Native prerelease scope:** use provider-compatible labels such as `preview1`
or `preview03`, matching the Gerber/NovaModuleTools release workflow.
PowerShellGet 2.2.5 rejects dotted labels such as `preview.2`; those labels remain
valid for PMU's semantic comparison but are outside this native update scope.
No Force, beta provider or alternate installer is introduced to bypass that
limitation. See
[current qualification](docs/architecture/TESTING.md#native-update-transition)
for successful Gallery evidence and the remaining gates. Historical G-4A3
acceptance does not qualify this replacement implementation.

Pass an installed module object, not just its name. The target need not be the
caller or already loaded:
Expand Down Expand Up @@ -58,10 +71,19 @@ not silently create a copy in another scope or fall back to CurrentUser when
AllUsers is not writable. Run in an appropriately authorized shell when system
permissions are necessary; PMU never requests elevation.

Installed-copy discovery ignores ordinary resource directories, including
`en-US` and `resources`, beside numeric version directories. A legacy flat
copy may coexist with a versioned installation without blocking discovery;
PMU does not migrate or select that flat copy as a versioned installation.
Numeric version directories and other directories containing the target
module's manifest remain subject to manifest and layout validation.

`IncludePrerelease` considers stable and prerelease releases together. Complete
versions use semantic prerelease ordering: `preview.10` follows `preview.2`,
and a stable release follows prereleases with the same numeric base.
Optional policy fields are `ExpectedRepositoryUri` and `VersionRange`.
Planning support does not imply that the current native provider can install
every selected version; the native label restriction above still applies.

### Private repositories

Expand Down Expand Up @@ -100,21 +122,27 @@ a new plan after restarting the shell or changing installation/repository state.
Repository selection is always caller-owned and source-bound. PMU never
searches another feed just because it contains the same package name. PSGallery,
public NuGet v3 repositories such as NuGet.org, and authenticated private
NuGet v3 repositories use the same PSResourceGet integration. General .NET
NuGet v3 repositories use the same PSResourceGet discovery integration.
Native installation has separate source/protocol qualification requirements;
successful discovery alone does not establish update support. General .NET
NuGet packages are not installable PowerShell modules merely because they can
be discovered or downloaded. Nexus is expected to work with compatible NuGet v3
capabilities, but no live Nexus qualification is implied.

## Replacement, recovery, and restart

Same-base transitions such as `2.0.0-preview.1` to `2.0.0-preview.2` and then
`2.0.0` require staged replacement because those versions share a numeric
Same-base transitions such as `2.0.0-preview1` to `2.0.0-preview2` and then
`2.0.0` share a numeric
installation directory. PMU validates the staged identity and GUID, preserves a
verified backup outside the Modules tree, records recovery state, and verifies
the installed file tree. Provider success alone is not proof of installation.
Equivalent dotted-label installation is not part of the Gerber/Nova native
update contract.

A cooperating PMU process holds an exclusive filesystem lock throughout the
operation. Interrupted replacements are checked on the next approved
A cooperating PMU process holds an exclusive filesystem lock during mutation.
The native worker acquires the same original installation lock through a
validated handoff; recovery cannot proceed while that worker still owns it.
Interrupted replacements are checked on the next approved
installation attempt. Recoverable failures trigger best-effort rollback;
unsafe or unsuccessful recovery stops further installation and preserves
available evidence. This is not an atomic installation or a power-loss
Expand Down
20 changes: 20 additions & 0 deletions RELEASE_NOTE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,30 @@ This file summarizes public cmdlet, CLI, configuration, and migration changes fo

### Changed

- Existing-module updates now use native `Update-Module` without Force or an
install fallback. Exact target/source, consent and restart requirements stay
in effect. Prerelease updates use native-compatible labels such as `preview03`;
dotted labels such as `preview.3` are not supported by the native installer.
Temporary repository setup also handles automatically registered defaults
without changing your repository settings.

### Deprecated

### Removed

### Fixed

- PMU now declares its qualified PSResourceGet 1.2.0 runtime dependency.
- Linux updates no longer falsely reject a live approving process. Legacy
Linux repository receipts require
[authorized manual review](docs/architecture/RECOVERY.md#interrupted-repository-transport)
rather than automatic cleanup.
- Contributor CI setup handles a missing Gallery registration and temporary
package download failures without changing the selected dependency versions.
- Updates now request the exact selected version in the form accepted by the
download provider, including prereleases such as `0.1.1-preview01`.
Dependencies must still resolve successfully before installation can finish.
- Update checks no longer fail merely because `en-US` or `resources` folders
coexist with a versioned module installation. Existing files are left intact.

### Security
6 changes: 6 additions & 0 deletions docs/PSModuleUpdate/en-US/Get-PSModuleUpdate.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,12 @@ two default to null. IncludePrerelease includes stable releases as well.
The explicitly selected scope must already contain the matching name and GUID;
PMU never creates a shadow installation or changes scope.

Ordinary resource folders beside numeric version directories do not count as
installed copies. This allows a versioned installation to coexist with resource
folders left by a legacy flat installation, without migrating or selecting the
flat copy. Numeric directories and directories containing the target module's
manifest still undergo manifest and layout validation.

The manifest supplies installed prerelease identity. The `Active*` fields
describe a matching module actually loaded in the session and are null when
the target is unloaded.
Expand Down
Loading
Loading