Chocolateyでのインストール直後にPATH変更を反映する

## やりたいこと

* Windows コマンドスクリプト(.cmd)や PowerShell(.ps1) で環境構築を自動化すべく、chocolatey で git や python をインストールし、続けて git や python を使って環境構築を完結させる。

「スクリプトなんだから続けて書けばいいだけじゃん。」と思うが、そこには落とし穴が。

## 課題・落とし穴

次の手順をスクリプトとして実装すれば git を使ってリポジトリのクローンまでできそうに思える。

> 1. chocolatey をインストール
> 2. choco install で git をインストール
> 3. インストールされた git コマンドでリポジトリをクローン

しかし、これを試すと、2. で git のインストールが成功しても、3. の git コマンド実行時に次のエラーが出て、期待通りに動かない。

```text
'git' は、内部コマンドまたは外部コマンド、
操作可能なプログラムまたはバッチ ファイルとして認識されていません。
```

実は、choco install で git をインストール時のログをよく見てみると、次のようなメッセージが出力されている。

```text
Environment Vars (like PATH) have changed. Close/reopen your shell to
 see the changes (or in powershell/cmd.exe just type `refreshenv`).
 The install of git.install was successful.
  Software installed to 'C:\Program Files\Git\'
```

要約すると次の通り。

> PATH などの環境変数の変更を反映するには、シェルを開きなおすか、`refreshenv` を使う必要がある。

例えば、git の場合は、PATH 設定に `C:\Program Files\Git\cmd` が追加されるが、レジストリは更新されていても、現在実行中のシェルの PATH は更新されていない。

## 対応方法

* choco install でパッケージをインストールした後、`refreshenv` を実行する。
    > この `refreshenv` は Chocolatery に付属のヘルパースクリプト。実体は Windows コマンドの場合は `RefreshEnv.cmd`、Powershell の場合は `Update-SessionEnvironment` 関数のエイリアス。

* Windows コマンドの場合の例

```
choco install -y git python
call refreshenv

git --version
python --version
```

* PowerShell の場合は、次の記事の通り、ヘルパーモジュールをインポートしておく必要もある。

> [How to refresh the environment of a PowerShell session after a Chocolatey install without needing to open a new session - Stack Overflow][refreshenv-powershell]

[refreshenv-powershell]:https://stackoverflow.com/questions/46758437/how-to-refresh-the-environment-of-a-powershell-session-after-a-chocolatey-instal)

```powershell
$env:ChocolateyInstall = Convert-Path "$((Get-Command choco).path)\..\.."
Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"

choco install -y git python
refreshenv

git --version
python --version
```

## 参考情報

### レジストリ中の PATH 設定を取得する方法

#### Windows コマンドの場合

* 次のレジストリーを取得する。
    * "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" -> "Path"
    * "HKEY_CURRENT_USER\Environment" -> "Path"
* ただし、`%SystemRoot%` のような環境変数が埋め込まれている場合があり、そのまま PATH に設定すると正常に動作しなくなる場合がある。

```
@echo off
setlocal

REM Query PATH environment value in registry
:QUERY_PATH
    call :GET_REG_VALUE "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment" "Path"
    call :FILL_ENV_VALUE "%REG_VALUE%"
    set SYSTEM_PATH=%ENV_VALUE%

    call :GET_REG_VALUE "HKEY_CURRENT_USER\Environment" "Path"
    call :FILL_ENV_VALUE "%REG_VALUE%"
    set USER_PATH=%ENV_VALUE%

    echo. & echo SYSTEM_PATH = %SYSTEM_PATH%
    echo. & echo USER_PATH = %USER_PATH%
exit /B

REM Get registry value
REM %1 : reg key
REM %2 : reg value name
REM REG_VALUE : reg value of output
:GET_REG_VALUE <reg_key> <reg_value_name>
    reg query "%~1" /v "%~2"
    for /F "tokens=2* skip=2" %%a in ('reg query "%~1" /v "%~2"') do (
        set REG_VALUE=%%b
    )
exit /B

REM Fill up %ENV_NAME% with environment value
REM %1 : string
REM ENV_VALUE : string of output
:FILL_ENV_VALUE <string>
    for /F "tokens=*" %%s in ('echo "%~1"') do (
        set ENV_VALUE=%%~s
    )
exit /B
```

#### Powershell の場合

```powershell
$SystemPath = (Get-ItemProperty -Path "Registry::HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment").Path
$UserPath = (Get-ItemProperty -Path "Registry::HKCU\Environment").Path
Write-Output "`$SystemPath = $SystemPath"
Write-Output "`$UserPath= $UserPath"
```

## 関連記事

* [How to refresh the environment of a PowerShell session after a Chocolatey install without needing to open a new session - Stack Overflow][refreshenv-powershell]
* [使うかもしれないレジストリ知識](http://var.blog.jp/archives/24991160.html)
* [reg query](https://docs.microsoft.com/ja-jp/windows-server/administration/windows-commands/reg-query)
* [変数にレジストリキーの値を読むのに苦労した](https://stackoverrun.com/ja/q/6114300)