しばたテックブログ

気分で書いている技術ブログです。

PowerShell上でdockerコマンドの自動補完を行うposh-dockerモジュールについて

PowerShell Advent Calendar 2016 6日目の駆け込みに軽いネタを。

qiita.com

はじめに

Docker for Windowsのサイトを何か更新がないかと眺めていたら新しいモジュールが紹介されているのを発見しました。

docs.docker.com

posh-dockerモジュール

github.com

こいつはタイトルの通り、単体の機能を持たず、PowerShell上でdocker.exeを実行する際のタブキーによる自動補完を提供するものになります。

いまのところdockerコマンドのサブコマンドと、Dockerイメージ名・コンテナ名の補完をしてくれる様です。
動作についてはGitHub上にあるGifアニメーションを見てもらえば一目瞭然かと思います。

インストールおよび使用方法

インストールはInstall-ModuleするだけでOKです。

Install-Module posh-docker -Scope CurrentUser

使用するにはImport-Moduleを明示的に実行する必要があるので、予めプロファイルに以下のコードを仕込んでおけばよいでしょう。

Import-Module posh-docker

内部動作と注意事項

ざっとソースを読んだ限りだと、TabExpansion2関数を直接差し替えてコマンドの自動補完を実現しています。

このため他の自動補完を行うモジュール(例えばposh-gitなど)との組み合わせによってはうまく動かないかもしれません。
TabExpansionPlusPlusを使っている場合も気を付けた方が良いかもです。

一応私の環境では普通に動いてくれました。

まとめ

Advent Calendarに間に合わせるために軽くまとめてみました。

Windows上でdockerコマンドを多用する方は導入してみるのも良いかもしれません。

はじめてのPowerShellモジュール公開

PowerShell Advent Calendar 2016 3日目の記事です。

qiita.com

はじめに

先月簡単なPowerShellモジュールを作ってPowerShell Galleryで公開しました。

その際に公式なドキュメントが全然無く苦労したので本エントリで手順をまとめることにしました。

内容についてはあまり詳細には触れず最低限の手順だけを記載し、モジュールを作って公開するための第一歩になれば良いかなというスタンスで進めていきます。

作成したモジュールについて

最初に軽く前提の説明を。

偉大なる先駆者たち

すべてのきっかけは@mzpさんのこのツイートからはじまっています。

そして@p_ck_さんのVim版。

PSSushiBar

これらの影響を受けて私もPowerShell版を作りました。

PSSushiBarという名のPowerShellモジュールとして作成し、ソースはGitHubに上げています。

github.com

はじめてのPowerShellモジュール公開

ここから本題に入ります。

このPSSushiBarを例に、モジュールの設定からPowerShell Galleryへの登録、モジュール公開までの手順を順に説明します。

0. 前提条件

今回は64bit版 Windows 10、PowerShell 5.1の環境で試しています。

モジュールの機能はPowerShell 2.0から、モジュール公開のための機能(PowerShellGet)はPowerShell 3.0から利用可能で、古いバージョンではパラメーターの名称や手順が異なる場合がありますのでご注意ください。

1. PowerShell Galleryのユーザー登録

PowerShell Galleryでモジュールを公開するにあたって最初にユーザー登録が必要です。

ユーザー登録にはMicrosoftアカウントを使用しますので予めアカウントを作っておいてください。


はじめに、ブラウザでPowerShell Galleryにアクセスし、画面右上のRegisterをクリックします。

f:id:stknohg:20161123231840p:plain


サインインの方法を聞かれますので、Personal accountを選びます。
(企業アカウントであればWork or school accountを選ぶことになるでしょうが今回は割愛します)

f:id:stknohg:20161123231920p:plain


契約条件の更新が出た場合はそのまま次へ

f:id:stknohg:20161123232152p:plain


アクセス許可を求められるのではいを選択します。

f:id:stknohg:20161123232230p:plain


続けてPowerShell Galleryで使うユーザー名(私はstknohgにしています)を入力しRegisterをクリックします。

f:id:stknohg:20161123233325p:plain


これでユーザー登録は完了し以下の画面に遷移します。

モジュールを公開するには後述するNuget API Keyが必要になりますので、View your profileをクリックしてアカウント情報を確認します。

f:id:stknohg:20161123232324p:plain


画面下部の認証情報にAPI Keyがありますのでこの内容を控えておきます。

f:id:stknohg:20161123232520p:plain


2. モジュールの設定

次に作成するモジュールの設定について説明します。

本エントリではモジュール作成自体の詳細には触れません。
そちらに関しては、PowerShell 4.0時点の内容ですが、ぎたぱそ先生の以下の記事が非常に有用なので参考にしてください。

tech.guitarrapc.com

本エントリで必要な前提知識

モジュールの公開に必要な前提知識について軽く説明しておきます。

  1. モジュールマニフェスト

    • 自作したモジュールを公開するためにはモジュールマニフェスト([モジュール名].psd1)ファイルが必要になります。
    • モジュールマニフェストはモジュールのバージョン等のメタ情報を記載するファイルとなり、名前は必ず[モジュール名].psd1にし、モジュール全体のルートディレクトリに配置する必要があります。
  2. モジュールの種類とモジュールマニフェスト

    • 先述のぎたぱそ先生の記事にある通り、モジュールはScript ModulesBinary ModulesManifest ModulesDynamic Modulesの4種類存在しています。

    • この中で公開可能なのはScript ModulesBinary ModulesManifest Modulesの3種類です。
      モジュールを公開するのにモジュールマニフェスト(*.psd1)が必須であるなら全てManifest Modulesになると思われがちですが、モジュールマニフェストの記載方法よってモジュールの種類が分かれます。

    • モジュールの種類を何にするかはその用途や規模によって変わると思いますので適宜選択してください。 よくわからなければとりあえずManifest Modulesにしておいて良いと思います。

ディレクトリ構成

PSSushiBarのディレクトリ構成は下図の様になっています。

f:id:stknohg:20161202061937p:plain

GitHubでソースを公開しているため、README.mdLICENSEがソースコードのルートに存在していますが、モジュールの公開で使うのはモジュールマニフェスト(PSSushibar.psd1ファイル)のあるPSSushibarディレクトリになります。

PSSushibar.psm1がモジュールのソースでここに必要な関数等が記述されています。

モジュールを公開するとモジュールマニフェストのあるディレクトリ配下のすべてのファイルがPowerShell Galleryにアップロードされダウンロードおよびインストール可能になります。

アップロードされるファイルを一部除外するといった例外を設けることはできません。
このため、上図の様にソースコードのルートとは別にモジュールのルートディレクトリを分けておき、アップロードしたくないファイル(PSSushiBarではREADME.mdなど)を別に分けることをお勧めします。

モジュールマニフェストの作成

モジュールマニフェストは基本的にNew-ModuleManifestコマンドレットを使って作成します。
指定可能なパラメーターは多くありますが、最低限-Pathパラメーターだけ指定すればOKです。

New-ModuleManifest -Path [モジュールマニフェスト(*.psd1ファイル)のパス]

実行例)

New-ModuleManifest -Path .\PSSushibar.psd1

コマンドレットを実行するとモジュールマニフェストが生成されますので、テキストエディタ等でその内容を適宜編集します。

モジュールマニフェストの内容について

PSSushiBar.psd1の内容を基に最低限設定が必要な項目について触れます。

個別の項目の詳細についてはMSDNの How to Write a PowerShell Module Manifest を参照してください。

#
# Module manifest例
#
@{
    GUID = '********-****-****-****-************' # 自分のGUIDを設定する
    ModuleVersion = '1.0'
    Description = '🍣'

    Author = 'stknohg'
    CompanyName = 'stknohg'
    Copyright = '(c) 2016 stknohg. All rights reserved.'

    NestedModules = @('PSSushiBar.psm1')

    # TypesToProcess = @()
    # FormatsToProcess = @()
    FunctionsToExport = @('Get-SushiCount', 'Start-SushiBar', 'Stop-SushiBar')
    # CmdletsToExport = @()
    # VariablesToExport = '*'
    # AliasesToExport = @()

    PrivateData = @{
        PSData = @{
            ProjectUri = 'https://github.com/stknohg/PSSushiBar'
            LicenseUri = 'https://github.com/stknohg/PSSushiBar/blob/master/LICENSE'
            # IconUri = ''
            # ReleaseNotes = ''
            Tags = @('🍣', 'sushi', '寿司')
        }
    }
}
項目 必須 内容
GUID モジュールの識別子となるGUIDです。必ずNew-ModuleManifestNew-GUIDで生成する様にしてください。
ModuleVersion モジュールのバージョンです。バージョンの記載方法は.NETアセンブリのそれと同じで [Major].[Minor].[BuildNumber].[Revision] になります。
Description - モジュールの説明。後から変更可能です。
Author - 著者名。後から変更可能です。
CompanyName - 会社名。後から変更可能です。
Copyright - コピーライト。後から変更可能です。
RootModule / NestedModules モジュールの種類に応じてRootModuleおよびNestedModulesを設定します。PSSushiBarではNestedModulesを指定しManifest Modulesにしています。*1
TypesToProcess 公開する型データ(*.type.ps1xml)がある場合設定します。
FormatsToProcess 公開するフォーマット(*.format.ps1xml)がある場合設定します。
FunctionsToExport 公開するファンクション名を設定します。
CmdletsToExport 公開するコマンドレット名を設定します。
VariablesToExport 公開する変数がある場合設定します。
AliasesToExport 公開するエイリアスがある場合設定します。
PrivateData - PowerShell Galleryに載せる各種情報などを記載します。

以上で公開までの前準備は完了です。

3. モジュールの公開

次に作成したモジュールをPowerShell Galleryに公開します。

PowerShellGet

モジュールを公開するにはPowerShellGetの機能を使用します。
PowerShellGetはPowerShell 5.0以降であれば標準でインストール済みです。

PowerShell 5.0以前の環境の場合はPowerShell Galleryみインストーラーがありますので、インストーラーをダウンロードしてインストールしておいてください。

モジュールマニフェストの検証

モジュールを公開する前にTest-ModuleManifestコマンドレットを使いモジュールマニフェストの内容を検証します。

Test-ModuleManifest -Path '[モジュールマニフェスト(*.psd1ファイル)のパス]'

実行例)

Test-ModuleManifest -Path "C:\Project\github\PSSushiBar\PSSushiBar\PSSushibar.psd1"

f:id:stknohg:20161130064054p:plain

エラーが出なければ成功です。

モジュールの公開

検証が終わったらPublish-Moduleコマンドレットを使用してモジュールを公開します。
パラメーターはいろいろありますが、最低限以下の様に-Path-NugetApiKeyを指定すればOKです。

-NugetApiKeyに先ほどPowerShell Galleryから取得したAPI Keyを設定してください。

Publish-Module -Path [モジュールのルートパス] -NugetApiKey [PowerShell Galleryから取得したAPIKey]

実行例)

# 横長になるのでスプラッティングを使っています
$params = @{
    Path = 'C:\Project\github\PSSushiBar\PSSushiBar'
    NuGetApiKey = '********-****-****-****-************'
}
Publish-Module @params

f:id:stknohg:20161203002827p:plain

内部でNugetを使っていますので、Nuget.exeの更新など聞かれる場合があります。
エラーが出ずに終了すれば完了です。

公開情報の確認

PowerShell GalleryMy AccountからManage My Itemsを選択すると、公開したモジュールの情報を確認できます。

f:id:stknohg:20161130064124p:plain

f:id:stknohg:20161130064243p:plain

必要に応じてWEB上からモジュール情報を変更することも可能です。

f:id:stknohg:20161130064314p:plain

4. モジュールのインストール

公開したモジュールはFind-Moduleで検索、Install-Moduleでインストールできる様になりますので実際に試して最終確認をします。

検索
Find-Module -Name PSSushiBar

f:id:stknohg:20161130064610p:plain

インストール
Install-Module -Name PSSushiBar -Scope CurrentUser

f:id:stknohg:20161130064636p:plain

確認
Get-Module PSSushiBar -ListAvailable

f:id:stknohg:20161202065845p:plain

5. モジュールのバージョンアップ

公開したモジュールをバージョンアップする場合はモジュールマニフェストのModuleVersionを新しいバージョンに書き換え、再度Publish-ModuleすればOKです。

まとめ

以上となります。

簡単にですが自作したモジュールをPowerShell Galleryに公開するまでの手順をまとめました。
手順や情報を調べるのが面倒ですが、一旦覚えてしまえばさほど苦労する手順ではありませんので是非みなさんも試してみてください。

参考にしたサイト

補足として本エントリを書くにあたって参考にしたサイトを記載しておきます。

*1:詳細についてはHow to Write a PowerShell Module Manifest参照してください

Pesterでカスタムアサーションを行う

【2017/11/01追記】

Pester 4.0.5から正式にカスタムアサーションを行うことができる様になりました。
詳細はこちらのエントリをご覧ください。

blog.shibata.tech

【追記ここまで】


前回の続きというわけではないのですが...
元ネタはこちら。

kamranicus.com

はじめに

大前提としてPester標準の機能としてカスタムアサーションを行うことはできません。

本エントリでは、元ネタにある通りPesterの内部動作をハックすることでカスタムアサーションを実現しています。
本エントリの内容はPester 3.4.0で動作確認していますが、将来Pesterの内部動作が変わった場合は実現できなくなる可能性があります。

このため現実的にはあまり使えない方法かもしれません。
こういった方法もあるよということで参考にしていただければと思います。

PesterのShouldコマンド

Shouldの基本動作

Pesterでのテストは以下の簡単な例の様に[テスト内容] | Should [動詞] [期待される値]の形で行うのが基本です。

Describe "テスト" {
    It "足し算のテスト" {
        1 + 1 | Should Be 2
    }
}

Shouldで利用可能な動詞は現在のところ以下となります。

動詞 内容
Be 等価比較(-eq)
BeExactly 等価比較(-ceq)
BeGreaterThan 大小比較(-gt)
BeLessThan 大小比較(-lt)
BeLike 文字列比較(-like)
BeLikeExactly 文字列比較(-clike)
BeOfType 型の比較(-is)
Exist ファイルなどの存在チェック(Test-Path)
Contain ファイル中の文字列チェック(Get-Content + -match)
ContainExactly ファイル中の文字列チェック(Get-Content + -cmatch)
Match 文字列中のパターンチェック(-match)
MatchExactly 文字列中のパターンチェック(-cmatch)
Throw 例外となるかチェック
BeNullOrEmpty NULLチェック

Shouldの内部動作

1. Pester[動詞]

これらの動詞はPester内部ではPester[動詞]という名前の関数で定義されており、ソースコード上は/Functions/Assertionsディレクトリに存在しています。

Beの場合であれば以下の様になっています。

# Pester/Functions/Assertions/Be.ps1 より
function PesterBe($value, $expected) {
    return ($expected -eq $value)
}
2. Pester[動詞]FailureMessage

また、テストに失敗したときのエラーメッセージ出力用にPester[動詞]FailureMessageという関数が使われています。
こちらもBeの場合は以下の様になっています。

# Pester/Functions/Assertions/Be.ps1 より一部抜粋
function PesterBeFailureMessage($value, $expected) {
    if (-not (($expected -is [string]) -and ($value -is [string])))
    {
        return "Expected: {$expected}`nBut was:  {$value}"
    }

    # ・・・後略・・・
}
3. NotPester[動詞]FailureMessage

最後にNotPester[動詞]FailureMessageという関数が使われています。
こちらはShould Not Beの様にNotと併せて使われた際のエラーメッセージ出力用の関数となります。

Beの場合の定義は以下。

# Pester/Functions/Assertions/Be.ps1 より
function NotPesterBeFailureMessage($value, $expected) {
    return "Expected: value was {$value}, but should not have been the same"
}
注意事項

Should内部では与えられた引数の順番で"テスト内容"($Value)か"期待される値"($Expected)かの判定を行っているため、上記3関数の引数の順序を変えてはいけません。

Pesterでカスタムアサーションを行う

ここまでの内容を踏まえて、Pesterでカスタムアサーションを行うには以下の3関数を定義してやれば良いことがわかります。

  1. Pester[動詞]
  2. Pester[動詞]FailureMessage
  3. NotPester[動詞]FailureMessage

元ネタではこれらの関数をモジュールに組み込むところまで行っていますが、最低限テスト中で必要な関数が定義されていればカスタムアサーションは可能ですので本エントリではそこまではしません。

カスタムアサーション例

Gistに簡単な例を上げました。
こちらの例ではHashtableを比較するためのBeHashという動詞を新たに定義しています。

gist.github.com

Pesterでカスタムアサーションを行うサンプル

実行結果

通常Be(-eq)ではHashtableの比較はできないため以下の様にエラーとなります。

f:id:stknohg:20161130032051p:plain

これが新たに定義したBeHashを使えばきちんと比較することができます。

f:id:stknohg:20161130032250p:plain

キーの値が異なる場合はちゃんとエラーとなります。

f:id:stknohg:20161130032524p:plain