Windows
chocolatey
Scoop
PackageManagement
OneGet

Scoop と PackageManagement を使った Windows 環境の構築

tl;dr

  • パッケージマネージャ マジ便利
  • ScoopPackageManagement は Windows で使えるパッケージマネージャ
    • PackageManagement
      • Microsoft製のパッケージマネージャ
      • 後ろで動いてるのは、NuGet や Chocolatey
    • Scoop
      • 主にCLI環境を整えるのにフォーカスしているパッケージマネージャ
      • 未登録パッケージでも、独自にBucketというリポジトリを登録すれば管理できる
  • この2つがあれば、大概のことは何とかなる

パッケージマネージャって何?

そもそも、Windows だけを使っていると、パッケージマネージャと言うものにあまり馴染みが無いかも知れない。

例えば、Firefox をインストールしようと思った時、従来の Windows ユーザであれば、

  1. ブラウザを開く ( 当然Firefoxではない )
  2. Google で Firefox と検索
  3. 公式サイトを表示
  4. ダウンロードリンクを探す
  5. ダウンロード
  6. ダウンロードフォルダを開く
  7. インストーラを実行
  8. インストーラーの指示に従い、進める
  9. 完了

という手順を踏んでいると思う。
また、アンインストールをする場合には、

  1. コントロールパネル or 設定画面 を開く
  2. プログラムと機能 or アプリと機能 を開く
  3. アプリを選択し、アプリをアンインストール

という手順が必要となる。

これが、パッケージマネージャを利用すると、
ターミナルを起動し、

# インストール
brew cask install firefox

# アンインストール
brew cask uninstall firefox

でサクッとできてしまう。これだけで、もう魅力的なのは分かる。

それ以外にも、

  • バージョンアップもコマンドから簡単
  • 依存関係も上手いこと解決してくれる
    • A 動かすには B が必要で、B 動かすには C が必要で... だから全部入れちゃえ!
    • え、C 消すの? A が使ってる B が使ってる C を、ホントに消しちゃうの?
  • コマンドで全てが完了できる
    • すなわち、手順の共有 が容易
    • すなわち、自動化 も容易

といった利点がある。

Windows で使えるパッケージマネージャ

これまでも、Windows で使えるパッケージマネージャは存在していた。

特に、Chocolatey は登録パッケージも豊富で、私の周りでは一番利用者が多かった。

PackageManagement (OneGet)

PackageManagement は、Windows 8 の頃に生まれた OneGet というパッケージマネージャの後継で、Windows 10 で正式に採用された比較的新しいパッケージマネージャである。

Github repo
Microsoft Docs

これ、「また新しいエコシステム作りやがって ... 」 という話ではなく、各パッケージマネージャの操作を抽象化し、共通のコマンドで扱えるようにしただけのもので、裏で動いてるのは NuGet や Chocolatey リポジトリから取得したスクリプト。

特徴

  • 何と言っても、Microsoft 公式
    • Windows10ではデフォルトで使える
  • Chocolatey が後ろにいる事もあり、最初からパッケージが充実している

導入

以下記事でコンパクトにまとまっている。

課題点

しかし、PackageManagement にはいくつか 使いづらいなぁ と思う点がある

  • コマンド体系が分かりづらい
    • サブコマンドではないので、何ができるのか、どうすればできるのかがぱっと分からない
      • 慣れの問題ではあるが …
  • Chocolatey の問題点を継承している
    • アンインストールを結構な確率で失敗する
    • パッケージが古いままのものも多い
      • これからエコシステムが発展していけば、あるいは
    • UAC がインストールの邪魔をする
      • 解決しているとの情報もあるが、未検証

今の所、PackageManagement はほぼ Chocolatey のラッパーとしてしか使っておらず、PowerShell Gallery や Web PI も対象に入れれば、また印象が変わるかも。

Scoop

Scoop は、主に CLI コマンドに特化したパッケージマネージャである。
Linuxに慣れ親しんだユーザが、Windows でもその使い心地を再現したいといったニーズに答えている。

Github repo

特徴

  • 公式リポジトリ ( Scoop では、Bucket という ) で、使いそうな CLI コマンドをほとんど網羅している
    • sudo curl grep sed less coreutils wget which jq git hub win32-openssh rsync netcat gcc make...
  • 無いものは、独自 Bucket を Github に置けば扱える
  • インストール先が ~/scoop 以下に集められて環境を汚さない (一部例外あり)
  • インストール / アンインストール / アップデートに失敗しない
  • コマンド体系も分かりやすい
  • インストール定義が JSON でできているが、これ自体がとても分かりやすい

Github の wiki が良くまとめられているので、そちらも参照するとよい

導入

以下記事では、導入の他、Chocolatey との比較もあって参考になる

ScoopでWindowsにおける開発環境構築を最適化しよう

課題点

Scoop にも、使っていてあれ?と思う点がある


2018/2/23 追記
下記の PATH が壊される件、経過観察してみたところ、どうやら Dashlane によるものだった可能性が濃厚。
Scoop 、犯人扱いしてごめんよ。


  • [未確定] PATH が壊される
    • 現在、導入した数名に発生している。再現性もない。調査中。
    • 念のため、PATH をバックアップして使用中。
    • 最近は起きていないので直ったかな?
  • 常に最新版
    • 開発環境等で使う場合は慎重に

どう使い分けるのか

現在、これら2つを使い分けながら Windows 環境を作っていて、それなりに満足している。
以下、思考フロー。

( Linux 環境で何かしたいなぁ ) ○。

⇒ Windows Subsystem for Linux (WSL) を使う。

( Linux のあのコマンド使いたいなぁ )○。

⇒ Scoop で探す or なければ独自 Bucket に追加。

( GUI のあるアプリ使いたいなぁ )○。

⇒ PackageManagement で探す。

( あのプログラム言語の環境つくりたいなぁ )○。

  • Case 1. ローカルで便利スクリプトを実行する、またはツールの実行環境・ビルド環境として必要
    ⇒ まずは Scoop で探す。Python, Go, Nodejs, Ruby 等はサクッと入れられる。

  • Case 2. チームで開発環境・バージョンを合わせる
    ⇒ Docker を利用。

  • Case 3. エディタの開発支援機能を使うための環境を用意する
    ⇒ 悩み中。いまは Scoop で雑に入れた環境を利用することで凌ぐ。

未解決問題

GUI アプリケーションのバージョンアップ問題

最近の GUI アプリケーションは、独自にアップデート機能を有している物が多く、パッケージマネージャはほぼインストール / アンインストールを自動化しているだけな感じ。まぁ、それでも十分だけど。

インストーラの設定デフォルト問題

Chocolatey の場合、内部でインストーラを Silent モードで起動しているだけのものも多いが、設定値がデフォルトになるのが、時々イラッとする。

管理対象多い問題

マシン移行を考えた時、PackageManagement, Scoop, WSL 側の apt と移行しないといけない環境が多い。
家 Mac では Ansible + ちょっと手作業 で移行できるようできているので、それと比べると煩雑。

Tips

Alias の上書き

Powershell には、デフォルトでいくつかの Alias が存在している。

PS> Get-Alias ls

CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           ls -> Get-ChildItem

多分、PowerShell の使い勝手を Linux ユーザにも馴染むようにとの心遣いであると思うのだが、オプションも違ったりとイマイチ心遣いが足りず、Scoop で入れた方のコマンドが使いたいという時がある。

PS> ls -la
Get-ChildItem : パラメーター名 'la' に一致するパラメーターが見つかりません。
発生場所 行:1 文字:4
+ ls -la
+    ~~~
    + CategoryInfo          : InvalidArgument: (:) [Get-ChildItem]、ParameterBindingException
    + FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand

そんな時は、PowerShell の Profile で Alias を無効化すると良い。

PS> notepad $profile

上記コマンドでメモ帳が立ち上がるので、その中に

profile.ps1
...

del alias:ls -Force

と無効にしたい Alias を記述する。
これで、Alias にヒットせずに、Scoop で入れたコマンドの方が呼ばれる。

PS> ls -la

drwxr-xr-x  9 Hoge Administrators 4096 Apr 25  2017 .
drwxr-xr-x 11 Hoge Administrators 4096 Nov 13 15:31 ..
-rw-r--r--  1 Hoge Administrators    7 Sep 23  2016 .gitignore
-rw-r--r--  1 Hoge Administrators  686 Oct  4  2016 README.md

Powershell 版が呼びたければ、直接 Alias の参照先コマンドを打てば良い

PS> Get-ChildItem

環境変数の設定

Powershell からシステム環境変数を変更するのは、結構手間がかかったりする。
そこで、Profile に関数を設定しておくと良い。

最新版は、gist に置いてある

profile.ps1
...
function setenv($key, $value, $target) {
  if (! $target) {
    $target = "User"
  }
  if (($target -eq "Process") -Or ($target -eq "User") -Or ($target -eq "Machine")) {
    $now = [environment]::getEnvironmentVariable($key, $target)
    if ($now) {
      $tChoiceDescription = "System.Management.Automation.Host.ChoiceDescription"
      $result = $host.ui.PromptForChoice("", "Already Exists. Overwrite ?", @(
        New-Object $tChoiceDescription ("&Yes")
        New-Object $tChoiceDescription ("&No")
      ), 1)
      switch ($result) {
        0 {break}
        1 {
          Write-Host "`r`nAborted." -ForegroundColor DarkRed
          return
        }
      }
    }
    [environment]::setEnvironmentVariable($key, $value, $target)
    [environment]::setEnvironmentVariable($key, $value, "Process")
  } else {
    Write-Host "Failure ! - Invalid Target" -ForegroundColor DarkYellow
  }
}
function setpath($value, $target) {
  if (! $target) {
    $target = "User"
  }
  if (($target -eq "Process") -Or ($target -eq "User") -Or ($target -eq "Machine")) {
    $item = Convert-Path $value

    $path = [environment]::getEnvironmentVariable("PATH", $target)
    $list = $path -split ";"

    if (! $list.Contains($item)) {
      $_path = $path + ";" + $item + ";"
      $newpath = $_path -replace ";;", ";"
      [environment]::setEnvironmentVariable("PATH", $newpath, $target)

      $_path = [environment]::getEnvironmentVariable("PATH", "Process") + ";" + $item + ";"
      $newpath = $_path -replace ";;", ";"
      [environment]::setEnvironmentVariable("PATH", $newpath, "Process")
    } else {
      Write-Host "Already Exists." -ForegroundColor DarkYellow
    }
  } else {
    Write-Host "Failure ! - Invalid Target" -ForegroundColor DarkRed
  }
}
PS1> setenv foo bar         # Add foo=bar as User Environment Variables
PS1> setenv foo bar Machine # Add foo=bar as Machine Environment Variables

PS1> setpath C:\hoge\huga   # Add 'C:\hoge\huga' to path as User Environment Variables
PS1> setpath C:/hoge/piyo   # '/' is convertible to '\'

ConEmu 等から $profile が読み込めない

ConEmu や、 Cmder は、独自の Profile を持っているため、$profile を読み込んでくれない。そんな時は、独自 Profile の方で $profile を読み込むように指示すると良い。

ConEmuのprofile
...

.$profile  # ← この一行を追加する