WindowsのPackageManagementについての知見まとめ

# 概要
Windowsをクリーンインストールする際にソフトを入れ直すのが大変です。
PackageManagementを利用すればコマンド一つでソフトをインストールできます！
が、PackageManagementの使い方に癖があるので、いつも使い方を忘れて、結局手動でインストールしたほうが早かったんじゃ、、と思ってしまうので、ここに今まで得た知見や自分なりの解釈をまとめます。間違いもあるかもしれない。

# 用語
PackageManagementの使い方を理解するために必要な用語

<dl>
<dt>PackageManagement</dt>
<dd>Microsoft Windows上で動作するパッケージ管理システムである。以前はOneGetという名称であった。Powershellから標準で利用することができる。</dd>

<dt>Module</dt>
<dd>ここでいうModuleとはPowerShellへ機能を追加するための機構。ModuleをインポートするとそのModuleに含まれるコマンドレットを使用することができるようになったりPowershellをカスタマイズできる拡張機能がつかえるようになったりする。</dd>

<dt>Package</dt>
<dd>PackageManagementでインストールされる単位。あるパッケージが他のパッケージに依存している場合は依存しているパッケージもインストールされる(パッケージによってはされない場合もある)。</dd>

<dt>Source</dt>
<dd>よく分かってない</dd>

<dt>Provider</dt>
<dd>PackageManagementにPackageを提供するもの。ソフトウェアとPackageManagementとの橋渡しをする。一つのProviderに複数のSourceを登録することができるらしいが、利用シーンは不明。</dd>

<dt>NuGet</dt>
<dd>.Netやjavascriptのライブラリがあるパッケージマネージャー。VisualStudioから利用できたりする。PackageManagementのProviderでもある。また、PackageManagementのProviderをインストールするときもNuGetのProviderからインストールを行っているようである。</dd>

<dt>PowerShellGet(Module)</dt>
<dd>PowerShellGetというModuleはInstall-ModuleといったModuleをインストールするコマンドレットが含まれる。
</dd>

<dt>PowerShellGet(Provider)</dt>
<dd>PowerShellGetという名前のProviderも存在する。つまりPowerShellGet(Module)のInstall-ModuleコマンドレットでインストールできるModuleはPowerShellGetをプロバイダとして指定すればPackageManagementからもPackageとしてインストールできる。
</dd>

<dt>Chocolatey</dt>
<dd>Windowsで最も利用されていると思われるパッケージマネージャ。有名なソフトならだいたいここからインストールできる。ChocolateyというProviderがあり、PackageManagementからChocolateyのパッケージをインストールできる。</dd>

<dt>ChocolateyGet</dt>
<dd>ChocolateyというProviderはインストールがよく失敗するのでこっちがメインで利用するProviderである。が、ChocolateyにはあるがChocolateyGetにはないパッケージも存在する。</dd>
</dl>

# 知見1：ExecutionPolicyを忘れずに設定しよう
Powershellスクリプトの実行ポリシー(ExecutionPolicy)はよく分からない人がうっかり変なスクリプトを実行しないようにデフォルトでは実行が行えないようRestrictedになっている。日常的にPowerShellを利用する人はPolicyをRemoteSignedとかにするのが常識のようになっているが、Windowsをクリーンインストールしたときとかは忘れがちである。下記コマンドを打てば設定は変更できる。

    Set-ExecutionPolicy -Force RemoteSigned

# 知見2:とりあえずProviderのインストールとアップデートをしよう
PackageManagementはProvider経由でソフトウェアをインストールするが、Providerはインストールしなければ使えない。また、標準でPowerShellGetのProviderはインストールされているが、Provider自体のバージョンが古いとインストールがうまくいかない事がある。ProviderのインストールはNuGet Providerから行っているようなので、まずNuGet　Providerをインストールする必要がある。Powershellを管理者権限で実行し下記コマンドを入力すれば各Providerをインストールできる。
    
    $providerList = @(
    'NuGet',
    'ChocolateyGet',
    'PowerShellGet',
    'Chocolatey'
    )
    $providerList|
    %{Install-PackageProvider $_ -Force}

PowerShellGetはInstall-PackageProviderコマンドレットで新しいバージョンをインストールしても、そのPowerShellセッションでは古いバージョンのままなので、一旦PowerShellを開き直す必要がある。

# 知見3:PackageをインストールするときはProviderを指定しよう
様々なProviderから統一的なインターフェースでPackageをインストールできるのがPackageManagementの利点ではあるのだが、現状、PackageをインストールするときはProviderを指定したほうが良さそう。同じPackage名が複数のProviderに存在するとインストールできないし、Providerの優先順位をつけるような機能もなさそうである。特にChocolateyとChocolateyGetには同じ名前のPackageがたくさんあるので、都度どっちからインストールするか指定する必要がある。また、ProviderをインストールしたあとにPowerShellを開き直すとProviderをImport-Providerコマンドレットでインポートしなければならないが、Packageインストール時にProviderを指定する場合はインポートする必要はない。
例えばGoogleChromeをインストールするときは下記のようにする。

    Install-Package GoogleChrome -Force -ProviderName ChocolateyGet

# 知見4:基本はChocolateyGetで、なければChocolateyを利用しよう
Chocolateyはインストールが失敗することが多い。全てChocolateyGetで網羅してくれればいいのだが、そうもいかないらしい。

    PS C:\Windows\system32> Find-Package googlechrome|%{$_.Name +":"+ $_.ProviderName}
    GoogleChrome:Chocolatey
    GoogleChrome:ChocolateyGet
    PS C:\Windows\system32> Find-Package googlejapaneseinput|%{$_.Name +":"+ 
    $_.ProviderName}
    GoogleJapaneseInput:Chocolatey

GoogleChromeはChocolateyにもChocolateyGetにもあるけどGoogle日本語入力はChocolateyにしかないことが分かる。
普通にFind-Packageだけ打つとPackageは見つけてくれるのだがProviderを表示してくれない。上記例のようにFind-Packageの結果をパイプで渡しProviderNameを表示する。

# 知見5:Package名はWebで探したほうが良いかもしれぬ
知見にもなっていないのだけど、Find-Packageで見つからないからと言って本当にないとは限らない。Package名が完全一致でないと引っかからなかったりする。Providerによって検索ロジックがちがうのだろうか。正直まだよくわかっていない。Package名をWebで探したほうが確実かもしれぬ。

    PS C:\Windows\system32> Find-Package "nodejs*"　←これだと見つかるのに

    Name                           Version          Source           Summary
    ----                           -------          ------           -------
    nodejs.install                 10.12.0          https://www.c...
    nodejs                         10.11.0          https://www.c...
    nodejs-lts                     8.12.0           https://www.c...
    nodejs.commandline             6.11.0           https://www.c...

    PS C:\Windows\system32> Find-Package "node*"　←これだと見つからない
    PS C:\Windows\system32>

#まとめ
クリーンインストールしたWindowsにChocolateyにあるソフトをインストールするには
1.ExecutionPolicyを設定する
2.Providerのインストールを行いPowerShellを開き直す
3.Web(https://chocolatey.org/)で欲しいソフトを検索
4.インストールしたいパッケージ名を特定
5.特定したパッケージ名でChocolateyGetを指定しインストールを行う
6.5で失敗したらChocolateyを指定しインストールを行う

とやるのが今の所一番早い。Find-Packageをもう少し使いこなせればいいんだけどな。

# 豆知識
以下、いろいろ調べているうちに得たはいいけど知らなくてもいいかなって知識
### Find-PackageProviderとGet-PackageProviderは返ってくるオブジェクトの型が違う。


    PS C:\Windows\system32> Find-PackageProvider|select -First 1|%{$_.GetType().fullname}
    Microsoft.PackageManagement.Packaging.SoftwareIdentity
    PS C:\Windows\system32> Get-PackageProvider|select -First 1|%{$_.GetType().fullname}
    Microsoft.PackageManagement.Implementation.PackageProvider

GetとFindの違いはインポートしているかどうかだけの違いだと思っていたので混乱した。

### beta版のPSReadLineを入れたいときは-AllowPrereleaseVersionsと-Force
PSReadLineというModuleが標準でインストールされている。ちなみにこのPSReadLineはバージョンが1.2でPackageManagement経由でもPowerShellGet経由でもないのでコマンドレットでアンインストールはできなかった。
バージョン1.2ではなくbata版のPSReadLineを使いたい場合はInstall-Packageコマンドレットに-AllowPrereleaseVersionsオプションと-Forceオプションをつけるとインストールできる。

    PS C:\Windows\system32> Install-Package -AllowPrereleaseVersions PSReadLine -Force
    Name                           Version          Source           Summary
    ----                           -------          ------           -------
    PSReadLine                     2.0.0-beta3      PSGallery        Great command line editing in the PowerShell consol...

    PS C:\Windows\system32> Get-Package "PSReadline"
    Name                           Version          Source                           ProviderName
    ----                           -------          ------                           ------------
    PSReadLine                     2.0.0-beta3      https://www.powershellgallery... PowerShellGet

    PS C:\Windows\system32> Get-Module "PSReadline"
    ModuleType Version    Name                                ExportedCommands
    ---------- -------    ----                                ----------------
    Script     1.2        PSReadline                          {Get-PSReadlineKeyHandler, Get-PSReadlineOption, Remove-PS...

上記のように1.2とbata版が両方入ることになる。PowerShellを開き直すと自動的にbata版のほうがImportされるようです。