обходить скрытие модуля PowerShell при использовании -Force на Import-Module внутри модуля psm1

контекст / настройка

Я работаю над довольно большим инструментом автоматизации сборки, который на 90% состоит из кода PowerShell.

Многие функции сгруппированы в файлах .psm1, которые загружают друг друга с помощью

Import-Module "$PSScriptRoot/AnotherModule.psm1" -Force

это казалось хорошей идеей для разработки / тестирования кода и т. д., но недавно я наткнулся на проблему с этим подходом.

поиск / проблема

всякий раз, когда загруженный модуль принудительно импортирует другой модуль, который ранее был загружен в родительской / внешней области, он больше не доступен в этой родительской / внешней области

теперь я столкнулся с проблемой, когда импорт необходимых модулей в начале скрипта не гарантирует, что импортированные модули действительно доступны во время выполнения скрипта, поскольку один из импортированных модулей может «скрыть» другой, ранее загруженный модуль: - /

пример

допустим, у нас есть три модуля: ModA, ModB, ModC:

ModA:

$ErrorActionPreference = "Stop"
Import-Module "$PSScriptRoot\ModB.psm1" -Force
// module A entrails

ModB:

$ErrorActionPreference = "Stop"
// module B entrails

ModC:

$ErrorActionPreference = "Stop"
Import-Module "$PSScriptRoot\ModA.psm1" -Force
// module C entrails

Следующие тесты pester демонстрируют это «скрытие модуля»:

Describe 'powershell module system tests' {

  # ModA -> Import ModB -Force
  # ModC -> Import ModA -Force
  $mod_path = Join-Path $PSScriptRoot "testmod"
  $mods = @("ModA", "ModB", "ModC")

  It "cleanup leftovers from previous tests" {
    Remove-Module $mods -ErrorAction SilentlyContinue
  }

  It 'is able to import ModA, which internally loads module B' {
    Import-Module "$mod_path\ModA.psm1" -Force
    Get-Module ModA | Should -Not -BeNullOrEmpty
    Get-Module ModB | Should -BeNullOrEmpty
  }
  
  It 'is able to import a second module already imported by the first one' {
    Get-Module ModA | Should -Not -BeNullOrEmpty
    Import-Module "$mod_path\ModB.psm1" -Force
    Get-Module ModB | Should -Not -BeNullOrEmpty
  }

  It 'still knows the first module' {
    Get-Module ModA | Should -Not -BeNullOrEmpty
  }

  It 'is able to import a thid module that also imports the first one' {
    Get-Module ModC | Should -BeNullOrEmpty
    Import-Module "$mod_path\ModC.psm1" -Force
    Get-Module ModC | Should -Not -BeNullOrEmpty
  }
  
  It 'does not know A or B any longer, as it has been hidden by C' {
    Get-Module ModA | Should -BeNullOrEmpty
    Get-Module ModB | Should -BeNullOrEmpty
  }

  It 'still knows C' {
    Get-Module ModC | Should -Not -BeNullOrEmpty
  }

  It 'cleanup leftovers from this test' {
    Remove-Module $mods
  }
}

отслеживание ошибок

чтобы точно определить проблему (ы), я попытался придумать еще один тест на приставку, который анализирует кодовую базу и проверяет, что ни один из «уже загруженных» модулей не удален из глобальной таблицы модулей при импорте другого:

Describe 'verify tool module system hierachy' {
  $tool_modules = Get-ChildItem $script:engine_path -Filter "*.psm1"

  function UNLoadAllToolModules {
    Remove-Module 'Tool-*'
  }
  function LoadAllToolModules {
    $tool_modules | Foreach-Object { Import-Module $_.FullName }
  }

  It "cleanup leftovers from previous tests" {
   UNLoadAllToolModules
  }
  
  It "loading of a module does not hide an already loaded module" {
    $errMods = $()
    $tool_modules | ForEach-Object {
      LoadAllToolModules
      $pre = Get-Module
      $mod = $_
      Import-Module $mod.FullName -Force
      $post = Get-Module
      $mods = Compare-Object $post $pre | Select-Object -Expand InputObject | Select-Object -Expand Name
      $mods | Foreach-Object {
        "import of '$mod' hides '$_'" | Write-Host 
      }
      if ($mods) {
        $errMods += $mod.Name
      }
    }
    $errMods | Should -BeNullOrEmpty
  } 
}

вопрос

есть ли способ распечатать / отладить глобальную таблицу модулей, как показано здесь, чтобы я мог выяснить, какой модуль загружается несколько раз / как изменяется дерево модулей после импорта?

решение?

в настоящее время единственный способ обойти эту «проблему» (впрочем, это, вероятно, плохой выбор дизайна для принудительного импорта модулей в другие модули) - либо отказаться от -force, либо использовать -global каждый раз при вызове Import-Module -Force модуля, содержащегося в этом пакете автоматизации сборки /орудие труда.


person mwallner    schedule 02.02.2021    source источник


Ответы (1)


Мой ответ на этот вопрос прост, но может оказаться неприменимым в вашей ситуации.

Области видимости в PowerShell довольно просты. Однако, когда вы начинаете пытаться делать такие вещи, я просто предлагаю вам просто загрузить все в глобальную область и покончить с этим. В противном случае вам придется много гоняться за своим хвостом.

На машине конечных пользователей использование глобальной области неприемлемо (по крайней мере, для меня). В системе автоматизации сборки использование Global scope может быть приемлемым, в зависимости от вашей ситуации, поскольку это все, для чего он будет использоваться.

Если вы посмотрите справку по командлету Import-Module, вы увидите это под параметром -Global:

> [!TIP] > You should avoid calling `Import-Module` from within a module.
> Instead, declare the target module as a nested module in the parent module's manifest. 
> Declaring nested modules improves the discoverability of dependencies.

The Global parameter is equivalent to the Scope parameter with a value of Global.

(Я исправил форматирование, чтобы оно работало).

Этот совет / совет также может сработать для вас.

person pauby    schedule 02.02.2021