Skip to content

Move to name-based RequiredModules with multi-module packaging #135

@blindzero

Description

@blindzero

Problem Statement

We need a standard, scalable distribution model for IdLE modules.

We are moving to name-based RequiredModules to support standard PowerShell dependency resolution (especially for PSGallery publication and third-party extensions). In repo/zip layouts (e.g., git clone or extracted GitHub archive), modules live under src/ and will not be discovered by name unless src/ is part of $env:PSModulePath.

At the same time, the current packaging approach (shipping many modules inside a single package under a non-module-root folder) prevents reliable Import-Module <ModuleName> behavior because discovery is PSModulePath-root based.

We want:

  • PSGallery scenario:
    • Install-Module IdLE installs IdLE plus its foundational dependencies (e.g., IdLE.Core, IdLE.Steps.Common)
    • Import-Module IdLE.Provider.* works by module name and pulls its declared RequiredModules
  • Repo/zip scenario:
    • Users import IdLE first, and IdLE bootstraps discovery so that subsequent imports by name work

Proposed Solution

Implement the distribution strategy in two parts:

  1. Packaging / Publishing overhaul (Monorepo → multiple published modules)
  2. Repo/Zip bootstrap for module discovery (Src-Mode via IdLE.init.ps1)

Part 1: Packaging / Publishing overhaul

Goals

  • Publish each module under src/<ModuleName> as a separate PSGallery module:
    • IdLE
    • IdLE.Core
    • IdLE.Steps.Common
    • IdLE.Steps.*
    • IdLE.Provider.*
  • Use name-based RequiredModules everywhere (no relative paths).
  • Ensure IdLE (meta module) uses RequiredModules = @('IdLE.Core','IdLE.Steps.Common').
  • Providers should not require Steps; they should require Core if they use Core APIs.
  • Keep external tool/module dependencies (e.g., EXO modules) as soft runtime dependencies if that is the current design (do not break import).

CI/Release requirements (high level)

  • Update packaging tooling so that builds produce one installable module package per module.
  • Update GitHub workflows to:
    • build module artifacts
    • run tests
    • publish modules (stable) to PSGallery in a controlled order if needed (Core/Common before dependent modules)
  • Define versioning strategy:
    • at minimum: consistent version bump across all published modules per release
    • or (optional later): independent module versioning (more complex)

Part 2: Repo/Zip bootstrap in IdLE.init.ps1

Enhance IdLE.init.ps1 to bootstrap module discovery for repo/zip layouts by idempotently extending $env:PSModulePath at process scope.

Behavior (detection)

On Import-Module IdLE:

  1. Determine the on-disk location of the imported IdLE module.
  2. If a repo/zip layout is detected:
    • detect a src directory one level above the IdLE module directory (stable relative path)
  3. If src exists and is not already in $env:PSModulePath, add it.

Rules

  • No global/system-wide environment changes (process/session only).
  • Idempotent: do not add duplicates.
  • If IdLE is installed via PSGallery (already discoverable), the init script should effectively do nothing.
  • Supported entry-point rule:
    • repo/zip layout: Import-Module IdLE first
    • direct provider/step import before IdLE is not supported in repo/zip layouts

Acceptance criteria

PSGallery / installed modules

  • Install-Module IdLE results in IdLE, IdLE.Core, and IdLE.Steps.Common being installable/discoverable by name.
  • Import-Module IdLE loads the meta module and its RequiredModules.
  • Importing a provider module by name (e.g., Import-Module IdLE.Provider.ExchangeOnline) loads its declared RequiredModules without requiring IdLE to have been imported first (in installed-module scenarios).

Repo/Zip layout

  • Import-Module <repo>/src/IdLE/IdLE.psd1 succeeds.
  • After that, Import-Module IdLE.Provider.* and Import-Module IdLE.Steps.* by name succeeds without manual PSModulePath edits.

CI/Release

  • Build produces separate module artifacts per module under src/.
  • Tests run and pass for the refactored dependency model.
  • Publishing pipeline can publish in a deterministic, documented order.

Implementation checklist (agent-ready)

  • Convert IdLE manifest from NestedModules to name-based RequiredModules (IdLE.Core, IdLE.Steps.Common)
  • Convert all module manifests to name-based RequiredModules (no relative paths)
  • Update packaging tool(s) to output one module package per module under src/
  • Update GitHub workflows to build/test/publish multiple modules
  • Define and document versioning strategy for multi-module publishing
  • Implement IdLE.init.ps1 repo/zip bootstrap (src detection + idempotent PSModulePath update)
  • Add Pester tests for init bootstrap logic and idempotency
  • Update documentation (single source of truth) for installation and repo/zip usage

Alternatives Considered

  • Keep a single “bundle” package and ship extra modules under internal folders.
    • Rejected: prevents standard Import-Module <Name> behavior and breaks third-party expectations.
  • Keep relative-path RequiredModules to support repo layouts without PSModulePath bootstrap.
    • Rejected: breaks standard dependency semantics and published module layouts; fragile for consumers.
  • Require users to manually edit $env:PSModulePath in repo/zip layouts.
    • Rejected: error-prone and inconsistent onboarding.

Impact

  • Breaking change (pre-1.0 acceptable):
    • Module manifests will change (NestedModules → name-based RequiredModules).
    • Consumers relying on internal layout/relative imports may need adjustment.
  • CI/Release process changes:
    • multiple PSGallery modules published per release
    • dependency ordering and versioning must be defined
  • Repo/zip usage gains a controlled, process-scope environment modification when importing IdLE (PSModulePath bootstrap).

Additional Context

Design decisions captured in this issue:

  • Distribution will be standard PowerShell:
    • each IdLE module is a standalone published module
    • dependencies are expressed via name-based RequiredModules
  • Repo/zip layouts are supported with the explicit rule:
    • IdLE must be imported first, then other modules can be imported by name
  • IdLE.init.ps1 performs repo/zip bootstrap only when a src layout is detected and only at process scope.

Metadata

Metadata

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions