tech.guitarrapc.cóm

Technical updates

PowerShell のAPIデザインガイドライン

この記事は、PowerShell Advent Calendar 2017 5日目の記事です。

qiita.com

昨日は @atworks さんの PSRemoting を用いたリモートプロセス実行でした。 qiita.com

3日目の前回 PowerShell のコーディングスタイルについて触れました。

tech.guitarrapc.com

次は、残りのAPI デザインを見てみましょう。

※ 2回に分けて書きましたが個人的には個人/チームが書きやすいようにすればいい話だと思っています。しかし、「曖昧だった基本的な指針がわからず困ってた方」にとって、ベストプラクティスはいい材料となると思います。PowerShell コミュニティは結構活発なので、コミュニティの中で皆様がよい PowerShell 生活を送られることを祈っています。

目次

C#のデザインガイド

Required Development Guidelines

https://msdn.microsoft.com/en-us/library/dd878238.aspx

Design Guidelines と Coding Guidelines がありますが、Design Guidelinesのみ触れます。

Design Guidelines Use Only Approved Verbs (RD01)

  • PowerShell の Cmdlet 規則である Verb-Nown 形式で公開する時に、あらかじめ定義されてある 動詞(Verb) を用いましょう
  • 動詞は用途ごとにクラス分離されているので、適切なものを使うといいです
    • VerbsCommon
    • VerbsCommunications
    • VerbsData
    • VerbsDiagnostic
    • T:System.Management.Automation.VerbsLifeCycle
    • VerbsSecurity
    • VerbsOther
  • どの動詞をいつ使うかのガイドラインも公開されています

(Design Guidelines) Cmdlet Names: Characters that cannot be Used (RD02)

  • Cmdlet には用いることができない特殊文字があります。その一覧を示しています
  • 私は基本的にアルファベットのみ用いるようにすることで沿えるので単純にそう捉えています
  • Parameters Names that cannot be Used (RD03)
  • PowerShell Cmdlet のパラメータには予約語があります。それを避けるようにしましょう
  • Confirm, Debug, ErrorAction, ErrorVariable, OutBuffer, OutVariable, WarningAction, WarningVariable, WhatIf, UseTransaction, and Verbose

(Design Guidelines) Support Confirmation Requests (RD04)

  • もしシステム変更を伴う操作を提供する場合は、PowerShell が持っている 確認機構を用いることをさしています
    • いわゆる ShouldProcess を指しています

(Design Guidelines) Support Force Parameter for Interactive Sessions (RD05)

  • 対話的実行を提供する場合に、Force パラメータは提供しましょう
  • これは PowerShell が自動化を念頭に置かれた言語なため、対話実行でそれを妨害することを防ぐためです
    • 以下のような操作が特に注意です
    • Prompt
    • PSHostUserInterface.PromptForChoice
    • IHostUISupportsMultipleChoiceSelection.PromptForChoice
    • Overload:System.Management.Automation.Host.PSHostUserInterface.PromptForCredential
    • ReadLine
    • ReadLineAsSecureString

(Design Guidelines) Document Output Objects (RD06)

  • 出力の記述です
  • C# のドキュメントXML で説明を公開するといいです

Strongly Encouraged Development Guidelines

次は強く要請するガイドラインです。

https://msdn.microsoft.com/en-us/library/dd878270.aspx

Design Guidelines と Coding Guidelines がありますが、Design Guidelinesのみ触れます。

(Design Guidelines) Use a Specific Noun for a Cmdlet Name (SD01)

Server のような汎用的な言葉ではなく、操作対象を明示した Process のような命名が好まれます。

(Design Guidelines) Use Pascal Case for Cmdlet Names (SD02)

  • Cmdlet 名は、PascalCase で表現しましょう。Clear-ItemProperty のほうが clear-itemProperty より好ましいです

(Design Guidelines) Parameter Design Guidelines (SD03)

  • 標準的なパラメータ名が公開されているのでここにあるものはそれを利用しましょう
    • たとえば、名前ならName、出力なら Output といった具合です
    • ただ、処理によっては ServiceName のようなより明示的な名前も提供したい場合があるでしょう。その場合は、パラメータ用プロパティにAlias属性 を用いることで [Alias("ServiceName")]のように表現できます
  • 単一要素を受けるパラメータには単数名を用いて表現しましょう
    • もし-es のような複数名を用いる場合は、そのbパラメータがいつでも複数要素を受け入れる場合にしましょう
  • パラメータはPascalCaseで。C# であれば Property を用いるので、C# デザインガイドと同じで違和感はないかと思います
    • errorAction や erroraction より、ErrorAction が好ましいです
  • パラメータの組み合わせで操作が変わる Cmdlet を提供する場合2つの方法があります
    • enum を用いて、enumごとに操作を分岐し パラメータを処理する方法
    • ValudateSet属性.aspx) を用いて、パラメータの入力を制約する方法
    • 私の経験上、複数の操作を提供する Cmdlet は作りたくなります。パラメータ1つだけが特定のパラメータ時に用いないようなの「単純な組み合わせ」であれば、ValidateSet が楽でしょう。が、複数の パラメータの組み合わせを ParameterSet で表現するのはおススメしません。Cmdlet を分離することを検討するといいでしょう
  • パラメータ名にはStandard Type を用いましょう
    • あらかじめ、どんな用途(Activity) にどんなパラメータが期待されるのかリストされています
    • Append など利用者が直感的に利用しやすい API デザインとして一貫性を保つため、該当するものを利用するといいでしょう
  • 強く型付けされた .NET Framework 型を利用する
    • Object を利用するということは、型に対する意識が強いということです
    • .NET Framework の型を意識して利用すると適切な型が入ることがほしょうされるため、よいでしょう
    • たとえば、URI には Uri 型を用いれば、String 型が入ってこないことを保証できます
  • パラメータの型に首尾一貫性をもたせる
    • たとえば、Process パラメータというのにInt16 型を当てた場合、他のCmdlet の Process パラメータで Uint16 を用いるのは避けましょう
    • 利用者の直感に反するので触り心地に大きく影響します
  • true/false をとるパラメータは避けて、Switch Parameter を用いましょう
  • Switch Parameter は、もし利用していれば true、なければ false とみなします
  • もし 3値 (true, false, Unspecified) が必要な場合は、Nullable<bool> が適切でしょう
    • 個人的に、Unspecified と null を合わせるのが適切なのかは一考の余地があります
  • 可能であれば、パラメータに配列を許容しましょう
    • 例えば Get-Process は Name に String配列を許容します
    • 利用者の使い心地として、複数回 Cmdlet を実行するより、1回で済む方がうれしいことは多いでしょう
  • PassThru パラメータのサポートを検討しましょう
    • Stop-Process のような値を返さない Cmdlet (Void型) であっても、時に結果オブジェクトが必要です
    • こういった場合に、PassThru パラメータを与えることで、結果オブジェクトを返すオプションを提供しましょう
    • Add, Set, New といった Verb の Cmdlet はサポートしているものが多いです
  • ParameterSet のサポート
    • Cmdlet は1つの目的のために作ります
    • が、時に1つの操作を複数の表現で呼べることがあるでしょう。つまり、パラメータの組み合わせということです
    • このパラメータの組み合わせの表現に、ParameterSet を用いることが多いです
    • ParameterSet を用いる場合、DefaultParameterSetCmdlet 属性に指定しましょう

(Design Guidelines) Provide Feedback to the User (SD04)

ユーザーは実行中ただ待つのは苦痛です。実行に対して何かしらのフィードバックを返しましょう。

  • WriteWarning, WriteVerbose, WriteDebug メソッドのサポート

    • もし意図しない結果が起こった場合は、WriteWarning メソッドで結果をユーザーに伝えましょう
    • もしユーザーがさらなる詳細情報を求める場合、WriteVerbose で結果を返しましょう。例えば、実行シナリオが意図した状態になっているかを伝えることもいいでしょう
    • 開発者がプロダクトサポートのために必要とする情報は、WriteDebug メソッドで返すといいでしょう
  • 長時間実行時の WriteProgress サポート

    • 長時間実行する場合、進捗を WriteProgress メソッドで表示するといいでしょう
  • Host Interface を用いた対話実行

    • 時に ShouldProcess 以外に、Host を通してユーザーとやり取りをする必要に迫られます。そんなときに Host プロパティを用いましょう
    • たとえば、PromptForChoiceWriteLine/ReadLine などです
    • もしCmdlet が GUI を生成しないなら、Out-GridView Cmdlet の利用も検討できます
    • また Cmdlet は、Console API は利用すべきではありません
  • Cmdlet ヘルプファイルの生成

    • Help.xml ファイルで、Cmdlet のヘルプを伝えることができます

(Design Guidelines) Advisory Development Guidelines

アドバイスとしてのガイドラインです。

https://msdn.microsoft.com/en-us/library/dd878291.aspx

Design Guidelines と Coding Guidelines がありますが、Design Guidelinesのみ触れます。

適用時は、Code Guideline も参考にしてください。

(Design Guidelines) Support an InputObject Parameter (AD01)

  • 特定の操作で良く用いられる名前があります。InputObject です
  • パイプラインからの入力をサポートしてプロセッシングするパラメータ名によく用いられ、.NET Framework のオブジェクトを取り扱います

(Design Guidelines) Support the Force Parameter (AD02)

  • Force パラメータを用いたユーザーの権限処理や対話を操作できるようにしましょう
  • Remove-Item Cmdlet の場合、通常は readonlyファイルを消せません。しかしForce パラメータを用いることで消すことができます
    • しかし、もしユーザーがそもそもそのファイルにアクセスする権限がない場合、Force をつけても何ら変わらず「失敗」します

(Design Guidelines) Handle Credentials Through Windows PowerShell (AD03)

  • Credential パラメータをサポートし魔装。このパラメータは PSCredential型を受け認証を処理することが期待されます
  • このサポートにより、ユーザーに対して自動的にポップアップを表示し、ユーザー名やパスワード入力ができるようになります
  • Credential パラメータには、Credential属性をあてます

Support Encoding Parameters (AD04)

  • テキストやバイナリを扱うときは、Encoding パラメータをサポートします

Test Cmdlets Should Return a Boolean (AD05)

  • Test- とつく Cmdlet は Boolean を返すことが期待されます

PowerShell のデザインガイド

実は、コーディングスタイルに含まれてしまっている部分が強いので、API デザインとしては存在しません。

ただし、Best Practice が存在します。

github.com

一度目を通してみると面白いのではないでしょうか?

  • Naming Conventions
  • Building Reusable Tools
  • Output and Formatting
  • Error Handling
  • Performance
  • Security
  • Language, Interop and .Net
  • Metadata, Versioning, and Packaging

ざくっと上げます。PURE とあるものは、議論の余地があるため記載しません。

Building Reusable Tools

再利用性に注目しています。

TOOL-01 Decide whether you're coding a 'tool' or a 'controller' script

  • 自分がツールを作ろうとしているのか、ツールの操作を作ろうとしているのか意識しましょう
    • なにかをするためのツールとして書かれている場合、re-usable でしょう
    • ツールをビジネスロジックに合わせて「操作」するために書かれている場合、re-usable ではないと考えられます

TOOL-02 Make your code modular

  • 処理を、関数にすることで、re-usable になります

TOOL-03 Make tools as re-usable as possible

  • 入力をパラメータで受け取り、パイプラインに出力する
  • この仕組みは re-usable さが最大限高まります

TOOL-04 Use PowerShell standard cmdlet naming

  • PowerShell の標準のネーミングをしましょう
  • Verb-Noun大事。Get-Verb Cmdlet で標準の Verb 一覧が見れます

TOOL-05 Use PowerShell standard parameter naming

  • 標準のパラメータ名を用いましょう

TOOL-06 Tools should output raw data

  • ツールの場合、Cmdlet の処理中に、データをなるべく触らず生で出力することをコミュニティとしては期待することが多いです
  • もし出力データを操作する場合でも、最小限にとどめましょう。そうすることで、多くのシーンで re-usable になります

TOOL-07 Controllers should typically output formatted data

  • 操作する場合、re-usable は主眼ではないので適切にわかるデータにフォーマットして返しましょう

WAST-01 Don't re-invent the wheel

  • 車輪の再発明だめ
  • 下の例は、Test-Connection $computername -Quiet で表現できます
function Ping-Computer ($computername) {
    $ping = Get-WmiObject Win32_PingStatus -filter "Address='$computername'"
    if ($ping.StatusCode -eq 0) {
        return $true
    } else {
        return $false
    }
}

WAST-02 Report bugs to Microsoft

  • バグは共有しよう

Output and Formatting

出力に関してです。

Don't use Write-Host unless you really mean it

  • Write-Host だめ。良く言われますね。Host にしか出力しないので、「見せるためだけ」「フォーマットするだけ」に利用しましょう
  • 特に Show Verb を使っていたり、Format Verb を使っている関数を書いた時にしか、使わないぐらいがいいです
  • なるべく他のWrite-* Cmdlet の利用を検討してください

Use Write-Progress to give progress information to someone running your script

  • ユーザーに何かしら進捗を示すとき Write-Progress が最適です
  • ただし、パイプライン上のなんでも流せばいいというものではありません。伝えたいことにしぼりましょう

Use Write-Debug to give information to someone maintaining your script

  • スクリプトのメンテナンスをする人に向けて、Write-Debug でメッセージを送ってください
  • $DebugPreference = "Continue" とすることで、Breakpoint で止まらず結果をみることもできます

Use CmdletBinding if you are using output streams

  • [CmdletBinding()] を使うだけで、出力ストリームを操作する -Verbose などが利用できるようになります

Use Format Files for your custom objects

  • カスタム型を使う場合は、modulename.format.ps1xml を使ってフォーマットを検討してください

Only output one "kind" of thing at a time

  • 1つの関数で、複数の型を返すことを避けてください
  • [OutputType()] で伝える型とのずれが生じるのは相当なコストをユーザーに強いることになります

Two important exceptions to the single-type rule

  • もし内部関数の場合は、複数の型を返すのはありです
    • $user, $group, $org = Get-UserGroupOrg のように分けて受け取れます
  • もし複数の型を返す場合、個別に Out-Default に包んで返すことでフォーマットが混在することを避けられます

Error Handling

エラー処理です。

ERR-01 Use -ErrorAction Stop when calling cmdlets

  • Cmdlet の呼び出し時は、-ErrorAction Stop をつけてエラー時に捕まえましょう

ERR-02 Use $ErrorActionPreference='Stop' or 'Continue' when calling non-cmdlets

  • Cmdlet ではない場合、呼び出し前に $ErrorActionPreference='Stop' を実行し、呼び出し後に$ErrorActionPreference='Continue' に戻しましょう
  • 特に自動化時に適切にエラーで止めることは重要です

ERR-03 Avoid using flags to handle errors

  • フラグで失敗制御はしないでください
try {
    $continue = $true
    Do-Something -ErrorAction Stop
} catch {
    $continue = $false
}

if ($continue) {
    Do-This
    Set-That
    Get-Those
}
  • try, catch で制御しましょう
    Do-Something -ErrorAction Stop
    Do-This
    Set-That
    Get-Those
} catch {
    Handle-Error
}

ERR-04 Avoid using $?

  • $? の利用は避けましょう
  • これはエラーが前回のコマンドで発生したか示すものではなく、前回のコマンドが成功したかみるだけです。この結果に関しては、ほぼ意味がないでしょう

ERR-05 Avoid testing for a null variable as an error condition

  • null チェックを全部いれるとかやめましょう

ERR-06 Copy $Error[0] to your own variable

  • 直前のエラーが $Error[0] に収められています。catch 句の $_ も同様です
  • ただ、次のエラーですぐに上書きされるので必要なら変数にいれてください
    • $Error 配列に過去のものは入っています

Performance

PERF-01 If performance matters, test it

  • PowerShell のパフォーマンスは、妙なくせだらけです
  • パフォーマンスかな、とおもったらテストしましょう
  • たとえば、以下の例なら2つ目が早いです
[void]Do-Something
Do-Something | Out-Null
  • いくつか方法が思いつく場合、計測しましょう

PERF-02 Consider trade-offs between performance and readability

  • パフォーマンスと読みやすさはトレードオフな場合があることを考慮してください
  • 例えば、式で表現とパイプラインで表現でも変わります
$content = Get-Content file.txt

ForEach ($line in $content) {
  Do-Something -input $line
}
Get-Content file.txt |
ForEach-Object -Process {
  Do-Something -input $\_
}

あるいは、.NET Framework を直接触ることでも変わります。

$sr = New-Object -Type System.IO.StreamReader -Arg file.txt

while ($sr.Peek() -ge 0) {
   $line = $sr.ReadLine()
   Do-Something -input $line
}

さらにこんな書き方もあるでしょう。

$handle = Open-TextFile file.txt

while (-not Test-TextFile -handle $handle) {
    Do-Something -input (Read-TextFile -handle $handle)
}
  • どれがいいかといえば、なるべく PowerShell に沿った書き方が読みやすいでしょう。が、基本的には .NET Framework のラッパーにすぎません
  • いくつものパターンがある中から、パフォーマンスとご自身の美学に沿って選択してください

Security

Always use PSCredential for credentials/passwords

  • Credential や パスワードには、PSCredentail を使います
  • SecureString でパスワードが保持されるため、基本的にこれを使いましょう
param (
    [System.Management.Automation.PSCredential]
    [System.Management.Automation.Credential()]
    $Credentials
)
  • どうしても生パスワードを、そこから拾う必要がある場合、メソッドから取得しましょう。なるべくさけてください
$Credentials.GetNetworkCredential().Password

Other Secure Strings

  • 他にも、Read-Host -AsSecureString でも SecureString を受け取ることgあできます
  • 万が一 SecureString を String にする必要があるなら、ZeroFreeBSTRE を用いてメモリリークを抑えてください
    # Decrypt a secure string.
    $BSTR = [System.Runtime.InteropServices.marshal]::SecureStringToBSTR($this);
    $plaintext = [System.Runtime.InteropServices.marshal]::PtrToStringAuto($BSTR);
    [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($BSTR);
    return $plaintext
  • もしディスクに認証を保持する必要がある場合、Export-CliXml を使ってパスワードを守ってください
    # Save a credential to disk
    Get-Credential | Export-CliXml -Path c:\creds\credential.xml

    # Import the previously saved credential
    $Credential = Import-CliXml -Path c:\creds\credential.xml
  • さらにもし、String がセンシティブでディスクに保持する必要がある場合は、ConvertFrom-SecureString で暗号化してください。ConvertTo-SecureString で戻すことができます。Windows Data Protection API (DPAPI)をつかっているため、同一Windowsマシンの同一ユーザーでのみ Decrypt できるので注意です。処理として、AES 共通鍵での暗号化もサポートしています
   # Prompt for a Secure String (in automation, just accept it as a parameter)
    $Secure = Read-Host -Prompt "Enter the Secure String" -AsSecureString

    # Encrypt to Standard String and store on disk
    ConvertFrom-SecureString -SecureString $Secure | Out-File -Path "${Env:AppData}\Sec.bin"

    # Read the Standard String from disk and convert to a SecureString
    $Secure = Get-Content -Path "${Env:AppData}\Sec.bin" | ConvertTo-SecureString

Language, Interop and .Net

VER-01 Write for the lowest version of PowerShell that you can

  • サポートする、もっとも低い PowerShell バージョンのために書いてください
  • ただし、新しいほどパフォーマンスメリットがあったりします
  • たとえば、PoweShell v3 では2番目の書き方のほうがかなり高速化されます
Get-Service | Where-Object -FilterScript { $\_.Status -eq 'Running' }
Get-Service | Where Status -eq Running

VER-02 Document the version of PowerShell the script was written for

  • #requires -version 3.0 といった形でサポートしているバージョンを明記してください
  • Module の場合、PowerShellVersion = '3.0' とマニフェストの psd1 に設定することで表明できます

余談 : 個人的に注意していること

私が特に多くの人から苦しいと耳にすることで、個人的に気を付けているものは次のものです。だいたい記事にしていたので参考にしていただけると幸いです。

  • [Object]型デフォルトに起因する型をつかった操作が影響受けやすいこと

tech.guitarrapc.com

  • $null の扱い

tech.guitarrapc.com

winscript.jp

  • パイプラインを通したときの実行速度と式の違い

tech.guitarrapc.com

tech.guitarrapc.com

  • 型の明示をしない場合の暗黙の型変換 (左辺合わせ)

tech.guitarrapc.com

  • 単一要素配列が返却時に自動的なアンラップがかかる

tech.guitarrapc.com

  • より安全に書くためには StrictMode の利用がいいでしょう

blog.shibata.tech

まとめ

PowerShell Script で書く場合も、C# で書く場合と同じように気を付ければ問題なさそうです。

特に、パラメータ入力、パイプラインが最も入り組んでいる印象が強いです。独自の構文$? はコンソールでの入力以外は使わないんですよねぇ。実際、私はほぼ使わないです。

PowerShell も .NET に限らず、一般的なプログラミング言語のやり方が生きます。言語自体の構文サポートの弱さやなど癖がありますが、ゆるく付き合うといいでしょう。

PowerShell のコーディングスタイル

この記事は、PowerShell Advent Calendar 2017 3日目の記事です。

qiita.com

新しい言語を触るときに気になるのが、その言語はどのように書くことを意図しているのかです。私が触ってきた言語の多くは「その言語の考えの基本」となるものを持っており、コーディングガイドライン上でもそれを明示していることが多いように思います。

PowerShell はどうなのでしょうか? 今回はPowerShell ではどのようなコーディングスタイル (本記事ではコーディング規則も同じ意味で用います)で書くといいのかを考えてみたいと思います。

思いのほか記事内容がながくなってしまったので、結論だけ見たい方は まとめ をどうぞ

※ 決してこの記事の内容が絶対正しいと思っていません。みなさんが書いていく中でどうすればいいのか、と思ったときの良いヒントになることを願っています。

軽い記事を書くつもりが大きくなりすぎたので、コーディングスタイルは主語が大きすぎということを感じました。補完しあう内容となる APIデザインも5日目に書きました。

tech.guitarrapc.com

目次

コーディングスタイル

ここでは言語を設計した側が意図している、あるいはコミュニティの中で共通認識として持っているものを指して考えたいと思います。

各開発チームごとにルールは別に設けられることもあるので、そこには触れません。言語として意図している、言語的にこうするといいとコミュニティの合意が取れている例と考えると読みやすいのでしょうか。優しい世界の優しいやり方に努めたいと思います。

さて、PowerShell に入る前に他の言語の例から見てみましょう。いろんな言語で、それぞれの考えがあって素敵です。

他言語の例 : C#

Microsoft が出しているコーディングガイドラインがあります。そこには次のようにコーディング規則の意義を説明しています。

C# 言語仕様では、コーディング標準は定義されていません。 ただし、このトピックのガイドラインは、サンプルおよびドキュメントを開発するためにマイクロソフトによって使用されます。 コーディング規則には、次の目的があります。 コードの見た目が統一されるため、コードを読むときに、レイアウトではなく内容に重点を置くことができます。 これにより、経験に基づいて推測することで、コードをより迅速に理解することができます。 コードのコピー、変更、および保守が容易になります。 コーディング規約により、C# のベスト プラクティスがわかります。

合わせて .NET Framework の API デザインにおける名前付けのガイドラインもあります。

.NET Framework 、特に C# を書き始める、どんなガイドラインで書けばいいか困ったなぁという時には .NETのクラスライブラリ設計 が非常に良い内容だと思っています。今は書籍が絶版で Kindle 版のみです。内容はAPIデザインガイドという意味で、他言語を扱うときにも参考になっていると感じます。

.NETのクラスライブラリ設計

C# のインデントスタイル は、 Allman Style で紹介されています。

他言語の例 : Swift

Swift にも、API Design Guidelines としてガイドラインがあります。

swift.org

Swift がGithub上でオープンに開発される中で、いくつかの Proposal は 検討の末リジェクトされていますが、それら理由にはガイドラインからの引用も多く見られます。徹底されているの素晴らしいです、ね。

Github/Swift - Commonly Rejected Changes

qiita.com

特に、「明瞭さは簡潔さよりも重要です」としたガイドラインは、Swift を書く上で自然と意識します。コミュニティの発信する情報も、多くがガイドラインに沿う努力がなされているように思います。

Clarity is more important than brevity. Although Swift code can be compact, it is a non-goal to enable the smallest possible code with the fewest characters. Brevity in Swift code, where it occurs, is a side-effect of the strong type system and features that naturally reduce boilerplate.

Swift のインデントスタイル は、 K&R 1TBS (OTBS) で紹介されています。このあたりの記事でもその辺が紹介されていますね。

Swift bracing

PowerShell のコーディングスタイル

さて、他言語の例をみたところで、PowerShell にも同様のガイドラインはあるのでしょうか? 答えは「あります」。ただ、PowerShell は Cmdlet(C#) と Script(PowerShell) として記述ができる*3ため長くなってしまいます。

そこで本記事においては、C# で Binary Cmdlet を記述する場合、PowerShell としてFuncion を記述する場合の2面から見てみましょう。

PowerShell Cmdlet を C# で書く場合のコーディングスタイル

まずは、PowerShell の Cmdlet = C# で記述するものとして見てみましょう。このガイドラインは、MSDN にて Microsoft から公開されています。

https://msdn.microsoft.com/en-us/library/dd878270.aspx

中身は、3つに分かれています。

  • Required Development Guidelines : 必須とするガイドライン
  • Strongly Encouraged Development Guidelines : 守ることを強く要請するガイドライン
  • Advisory Development Guidelines : アドバイスとしてのガイドライン

順にみていきましょう。

Required Development Guidelines

必須とするガイドラインです。

https://msdn.microsoft.com/en-us/library/dd878238.aspx

Design Guidelines と Coding Guidelines がありますが、Coding Guidelines のみ触れます。

(Coding Guidelines) Derive from the Cmdlet or PSCmdlet Classes (RC01)

  • PowerShell Cmdlet を C# で書くときに Cmdlet 型 か PSCmdlet 型を継承できます
    • Cmdlet の .NET Framework からの呼び出しの自由さよりも、PSCmdletのRunSpace内での実行を推奨しています
    • ここは若干微妙で、テストしやすさや Windows PowerShell Runtime と Core Runtime のずれがあるので今後の変更もあるかもしれません
  • また Cmdlet は public クラスである必要があります

(Coding Guidelines) Specify the Cmdlet Attribute (RC02)

  • Cmdlet には属性でどのように使うかを表明しましょう
  • verb-and-noun 表明により、公開するクラス名とCmdlet命名規則のずれをカバーできます。C# は クラスを PascalCase としていて、PowerShell はVerb-Noun ですからね!
  • デフォルトパラメータセットの表明やShouldProcessの表明もここで可能です

(Coding Guidelines) Override an Input Processing Method (RC03)

  • PowerShell Cmdlet は入力を3つのプロセスを通して処理します。Begin, Process, End です。これは良く説明されているので、さくっと紹介のみにとどめます
  • Cmdlet.BeginProcessing
    • This method is called one time, and it is used to provide pre-processing functionality
  • Cmdlet.ProcessRecord
    • This method is called multiple times, and it is used to provide record-by-record functionality
  • Cmdlet.EndProcessing
    • This method is called one time, and it is used to provide post-processing functionality

(Coding Guidelines) Specify the OutputType Attribute (RC04)

  • OutputType属性 を用いるといいでしょう
  • この属性を公開することで、パイプラインの先に型が伝搬します。しないと? Object になって、型が伝わらずインテリセンスが絶望的なことになります
  • ただ、PowerShell の Output は関数の返戻値型を強制しないため、ずれることがあるので注意です

(Coding Guidelines) Do Not Retain Handles to Output Objects (RC05)

  • Cmdlet は、WriteObject メソッドで出力をパイプラインに渡すのですが、このメソッドに渡す際にオブジェクトを内部で保持しないようにしましょう。ふつうにリークしたり予期しないエラーの原因になります。だめ

(Coding Guidelines) Handle Errors Robustly (RC06)

  • エラーハンドルです
  • 処理がこれ以上続行できない場合は、ThrowTerminatingErrorメソッドでエラーを返しましょう
    • もし例外が Cmdlet で取得できず漏れてしまった場合、PowerShell ランタイムがターミネートされます。しかもエラー情報 である ErrorRecord オブジェクトの情報が欠落するので意味不明な状況になって使い心地が最悪になります
  • もし処理ターミネートされるエラー出ない場合、ErrorRecord を WriteErrorメソッドに渡してハンドルしましょう
  • 例外の表明に使う ErrorRecord は、.NET GGramework のデザインガイドラインに沿って、すでにあるException で表明できるならそれを使いましょう。もし新規に作る必要があるなら、Exception型を継承する定石に乗ることを推奨します
    • また、Errorrecord は、エラーをグルーピングする ErrorCategory の提供してください
  • Cmdlet がスレッドを生成して、そのスレッドでエラーを起こした場合、PowerShell は例外をキャッチせずPowerShellプロセスをターミネートします。PowerShell でマルチスレッド処理を 書くのが苦しい原因になっていますね
  • もしデストラクタでUnhandled Exception が生じた場合は、PowerShell は例外をキャッチせずPowerShellプロセスをターミネートします。Dispose でUnhandled Exception を起こした時も同様です

(Coding Guidelines) Use a Windows PowerShell Module to Deploy your Cmdlets (RC07)

  • Cmdlet の提供は Module
    • SnapIn とか忘れましょう
    • 私自身 Moduleしか書きません。実際、PowerShell Get でのモジュール提供がインフラとして浸透した今、Module 以外で提供するのはエコシステムからも外れているのでつらいでしょう

Strongly Encouraged Development Guidelines

守ることを強く要請するガイドラインです。

https://msdn.microsoft.com/en-us/library/dd878270.aspx

これもDesign Guidelines と Coding Guidelines がありますが、Coding Guidelines のみ触れます。

(Coding Guidelines) Coding Parameters (SC01)

Parameter と読んでいるものは、public Property[Parameter()] 属性をつけたものが該当します。パラメータは、Static Member である必要がありません。

詳細は、Parameter Attribute Declaration で説明されています。

  • Support Windows PowerShell Paths

    • PSPath のような Normalize されたものを示しています
    • Path は String 型で表明してください
    • Path は、Alias として PSPath を与えてください。[Alias("PSPath") ですね
    • また、Path は ワイルドカード(*) をサポートしてください
      • もし ワイルドカードをサポートしない場合、LiteralPath パラメータとして用意しましょう
      • 個人的には、Path はパスワイルドカードのハンドルは、パス名に [ などのもじが入っていた時に崩れやすいため、LiteralPath のほうが意図した挙動にはしやすいかと思います
      • が、Path のほうが扱いやすいのも事実なので、提供側がどんな利用をするか手触りで選べばいいと思います
  • Support Wildcard Characters

    • もし可能であれば、パラメータはワイルドカード入力をサポートするといいでしょう
    • Process 名を探すときにGet-Process git* などのように、ワイルドカードサポートは利便性を大きく向上します
    • ワイルドカード入力をサポートしても、出力は複数になるかは一致するとは限りません。適切に扱ってください。例えば、Set-Location はパスにワイルドカードを受け付けますが、移動は一度だけです
  • Define ObjectMembers to Be Used as Parameters

    • もし Cmdlet のためのオブジェクトを用意する場合、そのオブジェクトを Parameter として受け入れられるようにすることを考えてください
    • また、独自の型で出力する場合は、PowerShell がユーザーに表示する際やパイプラインでメンバー渡すのに向いていないことがあります。この場合は、custom Types.ps1xml を作成して用意するといいでしょう。その場合の名前は、<Your_Product_Name>.Types.ps1xml が推奨されます
      • 例えば、FileInfo を表現するときにMode という ScriptProperty を用意してわかりやすくするなどが考えられます
      • ってありますが、つくらないですねぇ。私は。Cmdlet で用意するにしても、.NET で表現できた方が好きなので。Length の代わりに Count とかもあんまり好きじゃなかったり
  • Implement the IComparable Interface

    • IComparable Interface を用意することで、データ処理が楽になります
      • わたしは作りません。いるケースをかかないっていうのもありますがPowerShell でのデータ処理を複雑に行うことはないですねぇ
  • Update Display Information

    • <YourProductName>.Format.ps1xm を用いることで、表示を望む形に定義できます
      • 私は書かないです

(Coding Guidelines) Support Well Defined Pipeline Input (SC02)

  • Implement for the Middle of a Pipeline

    • Pipeline の途中でも動くように書くとパイプラインフレンドリーです。たとえば Get-Process の結果を受けるように書くときなどです
    • 個人的にも、パイプラインは都度ストリーム処理なので速度という意味ではパフォーマンスは不利ですが、メモリ効率性は高くPowerShellっぽくパイプラインでつないでさくっとデータ処理を書けるという意味では「書くのが楽」なスタイルなのでおススメです
  • Support Input from the Pipeline

    • ではどうすればパイプラインを受けられるのかですが、Parameter に、ValueFromPipelineValueFromPipelineByPropertyName 属性を足してください
  • Support the ProcessRecord Method

    • ストリームを都度処理することがパイプラインの特徴です
    • そこれで、ProcessRecord メソッドをオーバーライドすることで、ストリームからの都度入力を操作できます

(Coding Guidelines) Write Single Records to the Pipeline (SC03)

  • Pipeline はストリーム処理をすることに良さがるので、出力を都度シングルレコードで WriteObject メソッドで書き出すといいです
  • バッファリングして出力をためたりするのは、一度立ち止まって考えてからのほうがいいです
    • もしバッチ処理的に、出力をまとめた吐く場合は、System.Managemet.Automation.Cmdlet.WriteObject(System.Object,System.Boolean) の台に引数に trye を渡しましょう
    • 私自身、時々ためる処理を書いていますが、原則はパイプラインでの処理を速やかに後続に渡すようにしています

(Coding Guidelines) Make Cmdlets Case-Insensitive and Case-Preserving (SC04)

  • Windows PowerShell 自体が case-insensitive なため、処理もそれに従いましょう

Advisory Development Guidelines

アドバイスとしてのガイドラインです。

https://msdn.microsoft.com/en-us/library/dd878291.aspx

Design Guidelines と Coding Guidelines がありますが、Coding Guidelines のみ触れます。

(Coding Guidelines) Follow Cmdlet Class Naming Conventions (AC01)

  • Define a Cmdlet in the Correct Namespace
    • 適切な名前空間におきましょう
    • 名前空間は、.Commands をつけるといいです
      • おっと、そういえばですね
  • Name the Cmdlet Class to Match the Cmdlet Name
    • Cmdlet のクラス名は、<Verb><Noun><Command> と命名するといいです
      • たとえば、Get-Process Cmdlet なら、クラス名は、GetProcessCommand となります

(Coding Guidelines) If No Pipeline Input Override the BeginProcessing Method (AC02)

  • もしパイプラインからの入力を受け付けない 場合、BeginProcessing メソッドをオーバーライドして処理を書きましょう
  • パイプラインで動く場合、初めのCmdlet が実行されたときに、他のパイプラインの処理が動く前に呼び出されます
    • この begin の動きが、一番混乱を生みやすいですね

(Coding Guidelines) To Handle Stop Requests Override the StopProcessing Method (AC03)

  • StopProcessing メソッドを可能であればオーバーライドしてください。これによって、Stop信号を制御できるようになります
  • たとえば、長時間実行するようなCmdlet の場合なんですが、これまともに機能している例をあんまりみないんですよね。Job などの仕組みです

(Coding Guidelines) Implement the IDisposable Interface (AC04)

  • PowerShell は、EndProcessing を必ずしも呼びません。例えばパイプラインの途中でエラーが出たときとかですね
  • そこで、もし Dispose が必要なリソースを用いる場合は、IDisposable を実装して、明示的にDispose を呼び出してください
    • ここださいんですよねぇ。Cmdlet 書いていてつらい部分です

(Coding Guidelines) Use Serialization-friendly Parameter Types (AC05)

  • もしリモート先で動作するような Cmdlet を書く場合、Seriablize しやすくしましょう。以下がフレンドリーな型です
    • Primitiveな型
      • Byte, SByte, Decimal, Single, Double, Int16, Int32, Int64, Uint16, UInt32, and UInt64
      • Boolean, Guid, Byte[], TimeSpan, DateTime, Uri, and Version
      • Char, String, XmlDocument
    • Built-in 型
      • PSPrimitiveDictionary
      • SwitchParmeter
      • PSListModifier
      • PSCredential
      • IPAddress, MailAddress
      • CultureInfo
      • X509Certificate2, X500DistinguishedName
      • DirectorySecurity, FileSecurity, RegistrySecurity
    • 他いくつか
      • SecureString
      • Containers (lists and dictionaries of the above type)

(Coding Guidelines) Use SecureString for Sensitive Data (AC06)

  • センシティブなデータは、SecureString 使いましょう

余談 : PowerShell Core のコーディングスタイル

PowerShell は、現在 PowerShell Core が OSS として開発されています。これは C# で書かれています。そのため、この PowerShell 本体で用いられている C# のコーディング規則が公開されています。

C# Coding Guidelines

基盤実行のため、わかりやすさとパフォーマンスに重点が置かれていることが見て取れます。

Naming Conventions

  • メソッドの名前付けに関して、PowerShell自体の原則である Verb-Noun に合わせてLoadModule などのような - 抜きの名前付けを推奨しています
  • private field に関して Instance Filed のPrefixとして、_、Static Field は s_ (加えて readonly を用いる場合 static readonly という順番)、Thread Static Field は t_ という命名を示しています

Layout Conventions

C# 同様の Allman Style であることが見て取れます。

  • Avoid more than one blank empty line at any time
  • Avoid trailing spaces at the end of a line
  • Braces usually go on their own lines, with the exception of single line statements that are properly indented

Performance Considerations

LINQ のゴミをさけること、for/foreach の選択に関して触れています。

  • Avoid LINQ - it can create lots of avoidable garbage. Instead, iterate through a collection directly using for or foreach loop
  • Between for and foreach, for is slightly preferred when you're uncertain if foreach allocates an iterator

他にも、メモリアロケーションを避けるためのUtil として定義された static field の利用や boxing の回避のための Generics の利用など細かな内容が続きます。

Best Practices

この中で興味深いのは、C# の新しいシンタックスの利用は推奨されていることです。ただし既存のコードに適用する場合は、別のPR にすることを言っています。コード公開時のコードはずいぶんと古い書き方に感じたことを覚えています。

Use of new C# language syntax is encouraged. But avoid refactoring any existing code using new language syntax when submitting a PR as it obscures the functional changes of the PR. A separate PR should be submitted for such refactoring without any functional changes.


PowerShell Scipt で書く場合のコーディングスタイル

PowerShell Team からのものと、コミュニティからのものが公開されています。

PowerShell Team

https://blogs.technet.microsoft.com/pstips/2014/06/17/powershell-scripting-best-practices/

コミュニティ

github.com

また、別にPowerShell DSC のコーディングスタイルとしてHighQualityModuleGuidelines が公開されています。これは Module 特に DSC としてに注視しているため、ここでは扱いません。

github.com

PowerShell scripting best practices

PowerShell Team Blog の内容です。

  1. ドキュメントコメントを用いて、name, date, author, purpose and keywordsの記述をしましょう
  2. 可能な限りコメントを書きましょう。ただし行き過ぎないレベルで
  3. #Requires タグを用いて、サポートする PowerShell ミニマムバージョンを明示することで、後からわかりやすくしましょう
  4. Set-StrictMode -Version Latest コマンドを使って、初期化されていない変数アクセス、存在しないプロパティやオブジェクトアクセスを防ぎましょう
  5. シンプルだが意味のある変数命名をしましょう。camelCase がベストプラクティスです。($serviceName など)
    1. camelCase は ローカル変数のみ指しています。Parameter は指していません
  6. スクリプトの上部に、ユーザー定義変数を記述しましょう
  7. 可能であれば、CodeSigning を使います。(RemoteSigned / AllSigned を Execetion Policy に使います)
  8. Alias は使わないように。フルCmdlet名、かつパラメータを明示します
  9. backticks は使用をさけます。 パイプライン | や Splatting を活用します
  10. Filter left, format right にします
  11. .exe 拡張子を外部コマンド呼び出し時につけます。sc ではなく sc.exe という感じです
  12. $ErrorActionPreference = "SilentlyContinue" でパイプラインのエラーを消さないように。try. catch, finallytrap で構造的に制御します
  13. PowerShell 挙動制御変数 を使って、挙動を変えないようにします。$ConfirmPreference などです
  14. CmdletBinding を用いて、-WhatIf, -Verbose, -Debug をサポートしましょう
  15. Advanced Functions をサポートしましょう
  16. Verb-Noun 形式で表示します。また、標準に用意されたVerb(動詞)を使いましょう
  17. パラメータは汎用的な命名にし、必要なら初期値を入れます。(Server より ComputerName が望ましい)
  18. 1 Function = 1 動作にします。1動作をよく取り扱いましょう。(Linux と同様ですね)
  19. 関数は、常にオブジェクトを返して、文字列を返すことは避けます
  20. return キーワードの利用は避けます。パイプラインの中で自動的に返されます
    1. この挙動は結構悩ましいのお気をつけて
  21. Write-OutputWrite-Host より望ましいです。なぜなら、Write-Host はホストに書き出しますが、パイプラインに書かないからです
  22. Comment Base help を用いましょう
  23. 接続を確認するときは、Test-Connection-Quiet スイッチで使いましょう
  24. スクリプトは、書式をきれいにそろえましょう
    1. といいつつ、ここで書式の明示はありません
  25. どこから実行しても動くようにしましょう。$myInvocation.MyCommand.Path$PSScriptRoot が PowerShell 3.0 以降で利用できます
  26. 関数はテストしましょう。昇格やバージョン、存在有無のテストを含みましょう
  27. Production リリース前にテスト環境でテストしましょう
  28. Module や PowerShellGet を使って再利用しましょう
  29. Script を、作る、書く、使う、メンテナンス、レビューするパイプラインは用意しましょう
  30. こんな構成がシンプルです
    1. #Requires を書く
    2. パラメータを定義
    3. 関数を書きます
    4. 変数を定義します
    5. コードを実行します
    6. コメントベースヘルプを用意します

PowerShellPracticeAndStyle

コミュニティの共通認識になっている、Style Guideについて触れます。これは Python コミュニティを見習ってできた流れです。

The PowerShell Style Guide

いかについて触れられています。

  • Code Layout and Formatting
  • Function Structure
  • Documentation and Comments
  • Readability
  • Naming Conventions

(Code Layout & Formatting) Maintain consistency in layout

このガイドラインを強制するつもりがなく、一貫性を重視しています。1行の長さや行の数など、個人の主張があればどうぞ。という立場が貫かれています。

それを前提に参考にしてください。

(Code Layout and Formatting) Capitalization Conventions

次の用語の意味で読み進めてください。

  • lowercase - 全部小文字、単語分離なし
  • UPPERCASE - 全部大文字、単語分離なし
  • PascalCase - 各単語の初めの一文字だけ大文字
  • camelCase - 最初の単語を除く各単語の初めの一文字だけ大文字

PoweShell は、Case-Insensitive なので大文字、小文字を区別しません。しかし、PowerShell が .NET Framework でできていることから、次のルールに沿っています。

Capitalization Conventions - Framework Design Guidelines | Microsoft Learn

  • PowerShell は、public なアクセスのものはすべて PascalCase を使っています

    • Function, Cmdlet 名
    • Class
    • enum
    • Attribute(属性)名
    • パブリックフィールドやパブリックプロパティ
    • グローバル変数
    • グローバル定数
    • PowerShell のパラメータも.NET Class のプロパティなので PascalCase とします
  • PowerShell のキーワードは、lowerCase で表現します

    • foreachdynamicparam
    • -eq などのオペレータ
  • コメントベースヘルプは UPPERCASE で示します
    • .SYNOPSIS
    • .DESCRIPTION
  • Function(関数)名は、Verb-Noun 形式にします。また、Verb/Noun は、PascalCase とします
  • 特殊な2文字はキャピタルで示します
  • 関数やモジュール内の変数は camelCase とします。こうして、パラメータと区別しますが、ただの好みによります
  • 共有変数としての $Script:PSBoundParameters などはスコープ名を付けて明示します
  • 特殊な2文字を含むローカル変数は、adComputer などのように両方 lowercase にします

(Code Layout & Formatting) Always Start With CmdletBinding

  • 必ず関数は、[CmdletBinding()] param() で開始します
    • いわゆる AdvancedFunctions ということです
    • 必要に応じて、begin / process / end の各ブロックの1つは外してもいいでしょう
    • Pipeline 入力を受け取ることを考えてください
  • Open Brace { は同じ行に書きます。*4
  • Close Brace } は、自分音表に書きます。*5

(Code Layout & Formatting) Indentation

  • 4スペース インデントを用います。(ソフトタブ)
    • これは PowerShell ISE の挙動に沿っています
    • 行を並べる時には、文字列を縦にそろえることを検討してください
      • 個人的に、アラインはIDEがしないならしないですが、DSC のような宣言型では有用に思える時があります

(Code Layout & Formatting) Maximum Line Length

  • 1行115 文字以内に押さえます
    • PowerShell コンソールのデフォルトが120文字であることから示されています
  • 行を抑えるためには、Splatting が有用です

(Code Layout & Formatting) Blank lines

  • 関数やクラスは、2行あけます
  • クラス内のメソッドは1行あげます
  • 関連するワンライナーでは、空行は省略されることが多いです
  • 関数の集合や処理をまとめるのに、控えめに空行を使うのもいいでしょう
  • パラメータに渡すときは、: よりも明示的に渡しましょう
    • X : $variable=Get-Content $FilePath -Wai:($ReadCount-gt0) -First($ReadCount*5)
    • O : $variable = Get-Content -Path $FilePath -Wait:($ReadCount -gt 0) -TotalCount ($ReadCount * 5)

(Code Layout & Formatting) Spaces around special characters

  • カンマやセミコロン、{} の後に空白スペースをいれます
  • かっこ や[] の中に余計な空白スペースが入らないようにします
  • $( ... ){ ... } は、中の前後にスペースを1つ入れて読みやすくします
    • 逆に外にスペースは読みやすさのため「いりません」

(Code Layout & Formatting) Avoid using semicolons (;) at the end of each line.

  • PoweShell は セミコロン; を使えますが必須としません。編集やコピペされることを考え、いらないなら点けないようにしましょう
  • 同様に Hashtable の定義時も; はいりません

(Function Structure) Functions

  • return キーワードの利用は避けます
  • シンプルな巻子うを定義するときは、関数名とパラメータにスペースを空けます
    • function MyFunction ($param1, $param2)

(Function Structure) Advanced Functions

  • 命名は <verb- から始まるようにします
  • Noun (名詞) 部分は、1つ以上を結合した単語でPascalCase 、単数形書きます
  • return キーワードの利用は避けます
  • Process {} でのみ値を返し、Begin {}End {} では返さないようにします。これをしないと、パイプラインのメリットが損なわれます
    • return を用いないのはこの意味で意味が通っています

(Function Structure) Always use CmdletBinding attribute.

  • [CmdletBinding()] を必ず用います

(Function Structure) Always have at least a process {} code block if any parameters takes values from the Pipeline.

  • Pipeline からの入力があるなら、process {} ブロックを用意します

(Function Structure) Specify an OutputType attribute if the advanced function returns an object or collection of objects.

  • 返戻値の型を [OutputType(T)] 属性で 明示します。日うように応じて、パラメータセットとともに明示します
    • [OutputType([<TypeLiteral>], ParameterSetName="<Name>")]
    • [OutputType("<TypeNameString>", ParameterSetName="<Name>")]
      • 私は、[OutputType([<TypeLiteral>]) 形式がインテリセンス補完効くので好みです

(Function Structure) When a ParameterSetName is used in any of the parameters, always provide a DefaultParameterSetName in the CmdletBinding attribute.

  • ParameterSetName を用いる場合は、[CmdletBinding()]DefaultParameterSetName を明示します

(Function Structure) When using advanced functions or scripts with CmdletBinding attribute avoid validating parameters in the body of the script when possible and use parameter validation attributes instead.

  • Script 内部ではなく、パラメータの属性でバリデートを検討します

(Documentation and Comments) Comment

  • 常に更新しましょう
  • 英語でコメント書きます
  • 「何をしているか」ではなく「なぜ」を意識して書きます
    • うまく PowerShell らしく書くと、それ自体が説明しているように表現できます

(Documentation and Comments) Block comments

  • 一行ごとにコメントをつけたりはしないように
    • そういう場合は、ブロックコメントを検討してください
  • また、コメントが長い場合は、<# ... #> で複数行のコメントを表現できます

(Documentation and Comments) Inline comments

  • 気が散るためあまり好まれません
  • が、もし短いコードに説明を要する状況ではやる価値があります
  • 2文字以上コードとスペースを挙げて表現してください
  • また同じブロックの他のinline comment 行と頭をそろえると読みやすいでしょう

(Documentation and Comments) Documentation comments

  • Comment-based は、クラスの説明ではなく「何をする関数なのか」を書いてください
    • 必要以上に強い言葉は避けます
    • 短い説明に徹します
    • だれかに印象付けるために書いているわけではなく、どう使えばいいかを伝えるために書くのです
    • 外国語を書く場合、とくにシンプルな言葉でシンプル文で書くと、より ネイティブには意味が伝わります
    • 必要十分を目指しましょう
  • 書く場所は、関数の上ではなく中に書きましょう
    • コメントの更新を意識づけるため、末尾より、頭がいいでしょう
    • NOTES に詳細を書きます
    • Synopsisに概要を書きます
    • パラメータにコメントを書きます
      • 可能なら .PARAMETER に書きます
      • ただ、コード上に書いた方が良く更新される経験則があります
    • .EXAMPLE に利用例を示します

(Documentation and Comments) DOC-01 Write comment-based help

関数には毎度書きましょう。

function get-example {
    <#
    .SYNOPSIS
        A brief description of the function or script.

    .DESCRIPTION
        A longer description.

    .PARAMETER FirstParameter
        Description of each of the parameters
        Note:
        To make it easier to keep the comments synchronized with changes to the parameters, 
        the preferred location for parameter documentation comments is not here, 
        but within the param block, directly above each parameter.

    .PARAMETER SecondParameter
        Description of each of the parameters

    .INPUTS
        Description of objects that can be piped to the script

    .OUTPUTS
        Description of objects that are output by the script

    .EXAMPLE
        Example of how to run the script

    .LINK
        Links to further documentation

    .NOTES
        Detail on what the script does, if this is needed

    #>

(Readability) READ-01 Indent your code

  • インテンドは答えがありません。ただし、Script ではなくPoweShell に解釈させたときに結果が異なることがあります
  • 以下は、エラーが出ません
if ($this -gt $that) {
    Do-Something -with $that
}
  • 以下はエラーがでます
if ($this -gt $that)
{
    Do-Something -with $that
}
  • 構成要素を インテンドに収めるのがいいでしょう
ForEach ($computer in $computers) {
    Do-This
    Get-Those
}

(Readability) READ-02 Avoid backticks

  • Backticks ` は避けます
  • コミュニティは、あなたがBackticks を行継続 として使っていると感じます
    • しかし読みにくい、見落としやすい、ミスタイプしやすい傾向にあります
    • また Backticks の後ろにスペースを入れると動かなくなります
Get-WmiObject -Class Win32_LogicalDisk `
              -Filter "DriveType=3" `
              -ComputerName SERVER2
  • このような問題を引き起こしやすいため、テストやデバッグも難しくなる傾向にあります
  • 代替としてこのような Splatting を使うといいでしょう
$GetWmiObjectParams = @{
    Class = "Win32_LogicalDisk"
    Filter = "DriveType=3"
    ComputerName = "SERVER2"
}
Get-WmiObject @GetWmiObjectParams
  • 決して Backticks は嫌われていませんが、不便なことがあるので使う必要がないようにするといいでしょう

(Naming Conventions) Naming

  • 短縮した名前をコマンドやパラメータに用いるより、明示的な名前付けが好まれます
  • Expand-Alias を使うことで、いくつかを修正できますが、問題をなくすことは難しいでしょう

(Naming Conventions) Use the full name of each command.

  • PowerShell を使う中でフル名を学びますが、Alias は1つによって違います。(Linux ユーザーにとっての ls は、DOS ではdir です)
  • もし共有スクリプトの場合、汎用的に知られている名前がいいでしょう
    • ボーナスとして、Github ハイライトがかかりますよ
# Do not write:
gwmi -Class win32_service

# Instead write:
Get-WmiObject -Class Win32_Service

(Naming Conventions) Use full parameter names.

  • PowerShell には多くのコマンドがあるため、全コマンドを把握することはできません
  • パラメータ名を省略せずフルに書くことで、読む人がコマンドに詳しくなくても伝えることができるでしょう
  • また、不要なパラメータセットにまつわるバグを避けることができます
# Do not write:
Get-WmiObject win32_service name,state

# Instead write:
Get-WmiObject -Class win32_service -Property name,state

(Naming Conventions) Use full, explicit paths when possible.

  • 省略パスを用いないようにします。基本的に ... が有効に機能するのは、特定のパスにいる時だけです
  • .NET メソッドが [Environment]::CurrentDirectory を使い PowerShell の現在パスを尊重しないことを考慮しても、やはりこのような相対パス指定は避けるようにするといいでしょう
# Do not write:
Get-Content .\README.md

# Especially do not write:
[System.IO.File]::ReadAllText(".\README.md")

# Instead write:
Get-Content (Join-Path $PSScriptRoot README.md)

# Or even use string concatenation:
[System.IO.File]::ReadAllText("$PSScriptRoot\README.md")

(Naming Conventions) Avoid the use of ~ to represent the home folder.

  • ~ はプロバイダによって変わるので避けましょう
  • 代わりに、${Env:UserProfile}(Get-PSProvider FileSystem).Home が有用です
PS C:\Windows\system32> cd ~
PS C:\Users\Name> cd HKCU:\Software
PS HKCU:\Software> cd ~
cd : Home location for this provider is not set. To set the home location, call "(get-psprovider 'Registry').Home = 'path'".
At line:1 char:1
+ cd ~
+ ~~~~
    + CategoryInfo          : InvalidOperation: (:) [Set-Location], PSInvalidOperationException
    + FullyQualifiedErrorId : InvalidOperation,Microsoft.PowerShell.Commands.SetLocationCommand

余談 : PowerShell のインデントスタイル

そもそもこれを書きたかったのです。

PowerShell は、式モード(Script の解釈) の文法解釈においては Allman でも K&R 1TBS Style でも Stroupstrup もいけます。しかし、特に 「PowerShell.exe のコマンドモード」 と 「PowerShell ISEなどの式モード」 での構文解釈にずれがあるので、両方で不都合なくと思ったら K&R 1TBS Style が自然です。 私自身 C# と一緒に書くことが多かったので Allman で書いていましたが PowerShellでも動くように フォーマットのために ` 制御文字で調整はナンセンスかな、と思います。

同様に Stroustrup もないと思います。

K&R 1TBS Style

PowerShell の文解釈と式解釈にずれが生じません。

if ($this -gt $that) {
    Do-Something -with $that
} else {
    # NANIKA
}

BSD Allman

式解釈では問題ありません。が、PowerShell にはったときに、if の下の {else でエラーがでます。

if ($this -gt $that)
{
    Do-Something -with $that
}
else
{
    # NANIKA
}

Stroupstrup

式解釈では問題ありません。が、PowerShell にはったときに、else でエラーがでます。

if ($this -gt $that) {
    Do-Something -with $that
} 
else {
    # NANIKA
}

ただし、これは、普段どちらで書いているかなどに左右されますし、チームでの共通化がとれていればいいのでしょう。

ちなみに、コミュニティでは、Stroupstrup が意外と多い結果です。

Where to put braces · Issue #24 · PoshCode/PowerShellPracticeAndStyle · GitHub

余談 : ドキュメントフォーマッター

いまや デファクトスタンダードとなった、VSCode でもフォーマット可能です。

marketplace.visualstudio.com

このあたりのエディタに任せる、という判断が私は好ましいと思っています。コードフォーマットより、内容に注力したいですからねぇ。


まとめ

以上がPowerShell Coding Style です。コーディングスタイルは、あくまでもその言語らしさを第一に、どうやれば言語の良さを活用できるかを念頭に考えられています。

特に PowerShell コミュニティは、強制を避けつつアドバイスとしてというスタイルで醸成を図っています。みなさんのコード公開時に悩むということが減ることを祈ります。

軽くまとめます。

C# で書く Cmdlet の Coding Style

ほぼ、C# のコーディング規則通りです。フィールドにprefix当りが違う、PowerShell Cmdlet として属性をどうすればいいのか、override 時にどうするのかがメインメインになります。あとは、Path などの利用しやすさ、ですね。最も重要なのは、Verb-Noun 形式での公開でしょう。

  1. (Required) PSCmdlet を継承しましょう
  2. (Required) Cmdlet は Atrribute で挙動や命名を表現できます
  3. BeginProcessing/ProcessRecord/EndProcessing を用いて、入力を操作します
    1. (Required) Pipeline で用いないなら BeginProcessing のみで表現します
    2. (Required) Pipelin で用いるなら、ProcessRecord で入力を操作します
      1. (Strong) この時、入力ストリームはためずに即座に出力する方がいいでしょう
  4. (Required) 出力型は OutputType 属性で明示しましょう
  5. (Required) 出力オブジェクトは、内部で保持しないように
  6. (Required) 例外は ErrorRecord を用いて適切に扱いましょう
  7. (Required) Cmdlet は Module として配布しましょう
  8. (Strong) パスを用いるCmdlet の Parameter には、Path や LiteralPath を容易するといいでしょう
    1. (Strong) Path = Wildcard 入力を受け入れる場合に使います。PSPath を Alias として公開します
    2. (Strong) LiteralPath = Wildcard 入力を受け入れない場合に使います
  9. (Strong) 可能であれば Wildcard 入力を受け入れましょう
  10. (Strong) 自前の型を用意した場合、custom Types.ps1xml<YourProductName>.Format.ps1xm として用意するといいでしょう
    1. (Strong) <YourProductName>.Format.ps1xm で、出力された表示を適切にしましょう
  11. (Strong) 比較が必要ならIComparable を実装しましょう
  12. (Strong) パイプラインで動くようにしましょう
    1. (Strong) パイプラインの入力を受け入れるようにしましょう
      1. (Strong) その場合 ProcessRecord でハンドルします
    2. (Strong) パイプラインではシングルレコードを返します
  13. (Strong) Cmdletはケースインセンシティブにしましょう
  14. (Advice) 名前空間に、.Command をつけましょう
  15. (Advice) Cmdlet のクラス名は <Verb><Noun><Command> とします
  16. (Advice) StopProcessing をオーバーライドして操作を止めましょう
  17. (Advice) リソースを使う場合は、IDisposableを実装して明示的に呼び出します
  18. (Advice) リモート動作させるなら、シリアライズしやすいといいですね

※ Design Guide に含めれていてここにないのですが、PascalCase などのルールは、C# のコード基準に準しています。が、prefix を推奨したりしていて必ずしも準していません。

PowerShell で書く Funcion の Coding Style

インテンドスタイルを除き、多くは .NET で Cmdletを書いたらどうするのか、を基本コンセプトにしています。ただ、PowerShell の言語仕様として、; を省略できるといった部分はいらないなら書かなくてもいい。Backticks 避けようね、といったどうすれば読みやすいか、言語として書きやすいかを大事にしています。

こちらも最も重要なのは、Verb-Noun 形式での公開でしょう。

  • AdvancedFunction が標準になっています。ぜひ検討してください
    • [CmdletBindings] 属性がないと各種属性が書けないので当然かなと
  • 特に [OutputType] は、PowerShell のオブジェクト = 型を伝える、パイプラインの伝搬という性質に強く影響があるため必須です
    • ただし、OutputType と実際の返戻値は一致しなくても警告もエラーもでないので注意がいります。(ここがあまい)
  • #Requires タグは有益です。今後 Core が広まるなかで、より有益になるでしょう
    • ただ、実行時にしかわからないあたりが触り心地はよくないです
  • Set-StrictMode -Version Latest は 、あって困ることはないのでいれましょう
  • Verb-Noun 形式大事
    • しかし、外部に露出しない関数は、わたしは VerbNoun と普通のメソッド形式で書いています。実は。そこ Verb-Noun にしなくてもいいと思っています。(この例は PowerShell Team のコードにも良く見ますね)
  • ローカル変数はcamelCase、パラメータなどは PascalCase、これ広まってほしいですね
  • Splatting は結構目にします。やりすぎると読みにくくなりますが、多くの面で有益でしょう
  • 命名規則、省略を避けるという例は、PowerShell の目指す「わかりやすさ」に沿っており大事にしたいところです
  • Pipeline での動作がいらない時はbeing のみ書く
    • 省略したときの挙動との一致ですね
  • Pipeline での動作がいる場合は、process で返却する
    • end で返すことも多いのでふむですね
  • return はなるべく使わない
    • これはreturn をすると制御、関数が終わることを意図しています
    • が、ユーティリティ関数なら return を私は好んでいます

※ ここにはありませんでしたが、ASCII キーボードにおいて '" よりも Shift 入力が不要でタイポしにくいことから、文字列は ' が推奨されています。日本語キーボード? どっちでもいいですよねぇ。

*1:英語 : https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions

*2:英語 : https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/naming-guidelines

*3:これは語弊があり、実際は .NET Framework の言語であれば F# で書くこともできます。が、ここでは話をシンプルにするためにこの前提でいきます。

*4:議論 : https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/24

*5:議論 : https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/24

ServerlessConf Tokyo 2017 に参加してきた(キーノート編)

勉強会、カンファレンス、ミートアップ、言い方は違っても参加するたびに多くのことを学ばせてもらっています。普段、参加ログを書かないのですが、多くのことを人の参加記事から学んでいるので参加したセッションについて書いてみます。

※ この記事では キーノート 部分だけ書いておきます。午後の個別セッションはまた明日。*1

2017/11/2,3 と開催していた Serverless Conf Tokyo 2017 の Day2(11/3) に参加してきました。*2

http://tokyo.serverlessconf.io/home.htmltokyo.serverlessconf.io

2016 の初開催から2年連続で参加です。去年も思いましたが、これほどまでに参加して最高と思う機会は年に数回なので本当に素晴らしかったです。

http://2016.serverlessconf.tokyo/2016.serverlessconf.tokyo

目次

Software Productivity and Serverless

セッション資料 : 未公開

Nick Gottlieb - Serverless,inc.

Productivity を 「Developer Productivity」と「Software Productivity」に分けて問題定義を行ってから、Serverless でどういった課題が解決できるのかに触れるセッションでした。

Developer Productivity を上手く改善するために、これまで様々な方法が取れてきたことからセッションが始まります。

  1. Product / Engineering Process
    • どう開発をすすめるか
  2. DevOps/Atomation
    • どうすすめるかのやり方。よりAgileになどなど
  3. Outsourced Infrastructure
    • 自分でインフラを調達するのではなくほかから調達しましょう、とか
  4. Architectures
    • microservices も含めて、どのような構成にするのか

ただそれでも、年代を経ることでProductivity は徐々に下がってきており、推測として「複雑な開発に対して、十分なツール、方法がない?」ということに触れていました。

具体的な Productivity の障害となることを展開し、それに対してServerlessが助けとなりえることを9つ挙げていました。

  1. remove scaling from equation
    • スケーリングとか考えたくないよね。顧客の問題を解決することと、スケーリングは切り離して考える
  2. make it easy to go global
    • グローナルな提供をよりかんたんに。どこに提供するかと、アプリケーションの開発を切り離す
  3. make expelimentation cheap
    • 試すことをより安く、かんたんに。試すことに高価な対価を払うことは辞めたい
  4. tech should scale with a product
    • プロダクトの成長とともに、必要とされるものは変わってくる
    • そのたびたびで、チームの再教育など多くのことをする必要がある。会社をスケールさせるには、変化を受け入れて行く必要がある
  5. make tech as accessible as possible
    • OSS は偉大。AWSやAzure を使ったことがなくても、デプロイすればアプリが動く。このようなやりやすさ大事
  6. make it easy to customize tooling
    • やりたいことは「処理を書きたい」。Serverless のためのツールはよりかんたんにカスタマイズできる方がいい
  7. make data accessible
    • 多くのデータはアクセスしにくい
    • でも Serverless の event architechture がこれを解決する。イベントはデータ。イベントにデータが詰まってくるので、必要なデータの加工や利用が可能になる
  8. make code more reusable
    • Serverless のコードは、コード自身のメインロジックはどのServerless 環境でも変わらない。再利用が可能
  9. enable low/no-code development
    • ほとんどコードを書かずに開発できる、これが理想

感想

私の場合、なぜ Serverless を必要とするかの動機付けって目の前にある課題解決から始まることが多いため、では「どんなことを解決するのか」を網羅的に触れることは避ける傾向にあります。Productivity と Serverless は断片的に触れられる文脈を見る一方で、正面から考えることがなかったので刺激的でした。面白いです。

Serverless on Microsoft Azure

セッション資料 : 未公開

Chris Risner, Thiago Almeida - Microsoft

Serverless の定義に触れたうえで、

Scalable and reliable event based compute experience for code and workflows that accelerate the development of applications while hiding the infrastructure and providing auto-scale.

文脈から開発、環境のそれぞれに焦点を分けることから始まります。

code, workflow, development: 開発に寄与すること infrastructure, auto-scale: 環境に提供すること

ここから事例紹介をしつつ、パターンの紹介、デモでした。

例えば、Code に関してはAzureFunctions紹介をしつつデモサイトでの処理フローをVSCode でデバッグしつつ紹介してくれました。

  • Execute your code based on some events (イベントベースでのコード実行)
  • Azure Functions
    • Develop locally using best of class developer tools (ローカル実行による開発としてVSCode のデバッグデモ)
    • Most productivity through trigger and bindings (Trigger と Bindings による開発のしやすさ)
  • Functions triggers and bindings

デモに用いていたサイトがこちら。

https://carreview.azurewebsites.net/

同デモのリポジトリがこちら

github.com

Azure Functions Proxy で受け口 URL を抽象化しつつ、SPA なサイトでフロント待ち受け。サイトの状態は CosmosDB で保持。サイトから車の写真をアップロードすると、Blob Storage に行き、Event Driven で Functions に連携。 Compute Visionで写真の解析、判定を行って、本物の車の場合は車種などを入れつつ受け入れ、違う場合は Reject へ。リジェクトした場合は、Logic Apps 経由で、メールでどうするかの確認を行う。メールの選択肢によってそのまま Refect か Approve として受け入れるかの「人の処理の介入」まで触れているのがとても面白いデモでした。

他にも NaviTime の事例やFirst Gas の事例にも触れつつ、Azure Functions での構成を紹介してくれました。

microsoft.github.io

customers.microsoft.com

感想

Azure Functions はヘビーに使っていて相当好きなサービスです。その中で Logic Apps につないでコードレスに「インタラクティブな処理」を介在させるのがとても面白いですし刺激でした。Orchestration は DurableFunctions でかなり解消したので、EventDriven、FaaS の先としてインタラクティブとのつなぎこみが課題に感じていたので非常に良かったです。この選択は、現状 AZure が最も楽、かつ適切に組み込みやすいので着実に正当進化しつつ、利用の幅を広げていて素敵ですね。*3

Growing up Serverless

セッション資料 : 未公開のようです*4

Keisuke Nishitani - AWS

「なぜサーバーレスが必要となったのか」に触れつつ、どんな変化があり、どんな使われ方をされていて、困ることへのフレームワーク/サービスでのアプローチ まで網羅的に触れているセッションでした。

Lambda の実行ホストが、数時間でプールに戻されてパッチを当てられ続けているのは嬉しいですね。

SQUARE ENIXさんにおけるDQ11 の記念撮影の処理をオンプレからLambda に移行した事例紹介は、ちょうどいい感じで Lambda によるスケールやイベント処理が活用できる題材で面白いです。

  • 1分あたり200 - 300 イメージを処理
  • ピークで1分あたり6000イメージを処理
  • 処理時間が数時間から10数秒に
  • オンプレミスとうらべ1/20 までコスト削減

またサーバーレスを、Compute な Lambda だけでなく多くのコンポーネントに触れつつ紹介されているのが良かったです。

デリバリの変化、X-Ray の登場、Map&Reduce に PyWren と、スケールアウトの利便性と複雑性をバランスとっていくかを考えるじかんでした。

github.com

感想

他の方が触れない部分をきっちり抑えられているのと「提供する側としてどういう未来」が見えているのかを触れられているのが他の方とは角度の違う切り口で面白いセッションでした。

このあたりは著書にも垣間見えるので、読んでおくとセッションが面白いと感じました。

https://www.amazon.co.jp/dp/B071FZL2RZ/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1www.amazon.co.jp

keisuke69.hatenablog.jp

実際のところ、サーバーレスってコンピュートが皮切りに出てきましたが、SaaS や(フル)マネージドサービス、とどう分けるかはまだまだ議論がありそうに思います。(当日の様子もそうでした) 個人的には「イベント連携できるフルマネージドなサービス」はサーバーレスビルディングブロックにいれつつ、イベント連携できない場合はサービス連携程度にゆるくとらえています。なぜかというと、AWS だけ、Azureだけではなく、両者、あるいは他クラウドも連携させているからだと思います。今後どうなるかは分かりませんが、説明の時に考慮することはあっても普段の利用ではそこまで気にならないような気がします。

まとめ

明日へ続く。

esa にすべて書いてあるので、公開URL してもいいのですがそこはまぁ抜粋です。内容濃いセッションが多かった分、メモも多くなりました... いい一日でした。

*1:キーノートに関する感想記事が全然ないのですが、書いちゃだめって説明なかったと思うのですが... もしダメなら消します。

*2:tokyo.serverlessconf.io が毎年どんどんその年のになっていくのでこういう過去の紹介時はひと手間

*3:こっそりセッション中にデモサイトに車の写真を挙げておいたのですがVSCode のデバッグが効いたままだったらしく Pending のままでした。上手。

*4:おおよそこれに沿っていました Growing up serverless

Visual Studio 2017 で使っている拡張機能

Visual Studio Code も Visual Studio もExtensions と呼ばれる 拡張機能を使うことで便利な機能を追加できます。

Market Place 便利。

marketplace.visualstudio.com

あまりむやみに追加すると動作が重くなるのですが、便利なものを使わない手はないので必要なものを入れています。*1

ここではVisual Studio 2017で私が入れている便利拡張をまとめておきます。

目次

Extensions一覧

Recommend Name Purpose Desc
5 Azure DevOps Services | Sign In 学習されるインテリセンス VS2019の機能ですが、VS2017でも拡張機能をPreviewで使えます。
5 GitHub - StefanKert/BuildVision: A Visual Studio extension to visualize the building process. ビルド結果表示の高機能化 VS2017 対応に合わせてオリジナル作者から引き継がれました
残念ながらMarket Place版は2015のままです
5 Git Diff Margin - Visual Studio Marketplace Git の追跡結果と比較して差分のハイライト表示をしてくれます
5 Open on GitHub - Visual Studio Marketplace 選択ファイル/行の Github ページにジャンプ
5 SwitchStartupProject for VS 2017 - Visual Studio Marketplace Startup Project の変更がすさまじく楽になります
5 VSColorOutput - Visual Studio Marketplace Output Window に出力された文字に状態に応じて色が付きます。
5 Custom Document Well - Visual Studio Marketplace 縦タブが使いたくて! VS2015までは Productivity Power Tools で提供されていた機能が個別パッケージで提供されました。
5 Open UserSecrets - Visual Studio Marketplace .NET Core Console で UserSecretsを使いたくて ASP.NET Core MVC の Manage UserSecrets と同様の機能を .NET Core Console でも提供します
4 BlackSpace - Visual Studio Marketplace 末尾の不用意なスペースをハイライト、保存時に削除も可能です。 類似拡張 : Trailing Space Flagger - Visual Studio Marketplace
4 LetMeEdit - Visual Studio Marketplace .csproj を unload せずに編集できます。 EditProjのFolkで、2017に対応してくれています。
3 PowerMode - Visual Studio Marketplace タイプでコンボ、楽しくないですか? 画面が大きくなるほど重いです。4Kとかやばい。設定必須。
3 Fix Mixed Tabs - Visual Studio Marketplace ソフトタブ、ハードタブの混在ファイルを開いた時に統一できます。 VS2015までは Productivity Power Tools で提供されていた機能が個別パッケージで提供されました。
2 SlowCheetah - Visual Studio Marketplace Configuration Transform のおともに 使う人にしかいらないやつです。.NET CoreのDIで不要になった説もあります。
2 Open Command Line (32-bit) - Visual Studio Marketplace VS から.cmd や .ps1 が実行できるの便利 powershell x86 なのは注意

それぞれの拡張

Visual Studio IntelliCode - Preview

2018/12/17追記

インテリセンス、コード補完と呼ばれるコード支援機能をさらに支援します。

https://marketplace.visualstudio.com/items?itemName=VisualStudioExptTeam.VSIntelliCodemarketplace.visualstudio.com

公式曰く...。

IntelliCode は、使用する可能性が高いものを入力候補一覧の最上位に配置することによって時間を節約できます。 IntelliCode の推奨は、星の獲得数が 100 を上回る、GitHub 上の何千ものオープン ソース プロジェクトに基づいています。 コードのコンテキストを結合すると、入力候補一覧が調整されて共通のプラクティスのレベルが上がります。 IntelliCode はステートメント入力候補に限定されていません。 シグネチャ ヘルプも、コンテキストに合わせて最も可能性が高いオーバーロードを推奨します。 このプレビュー リリースでは、C# を使用している場合、IntelliCode にご使用のコードからパターンを学習させることができるため、独自のユーティリティ クラスやドメイン固有のライブラリ呼び出しなど、オープンソース ドメインにない型の推奨を行うことができます。 詳しくは、以下をご覧ください。

VS2017 では、自分のプロジェクトで認識させるので自分の手癖や共有コードの癖が出ます。 たとえば、Unity + UniRx で Subject に対する OnNext(T) は良く書くところですが、これがトップに来てくれる当りめっちゃ嬉しいです。(他にもありますが分かりやすいでしょう)

追記ここまで

Build Vision

最高というより空気、これなしのビルドは考えられないぐらいには必須拡張と化しています。使ってないビルドはもう考えられません。

github.com

ビルドが可視化されてなかったことに気付かされた時の絶望たるや。

これだけ Market Place に出ていませんが、発行のために動いてくれているので心待ちにしたりフィードバックをどんどん返しましょう。

github.com

発行までの Pre-release は VSIXGallery で取得できます。

vsixgallery.com

Git Diff Margin

Git 使ってますよね? ソースコードが git 上とどのような変更があったか気になりませんか?エディタ上で変更点が可視化されます。変更があれば視覚上で追えてgit 操作前に git で何を変更するか分かる、最高です。

marketplace.visualstudio.com

未保存が黄色、追加行が緑、変更が青、削除が赤で表示されています。削除内容も赤い三角から見えます。

Open on GitHub

ソースコードが Github 上でどのurl? 選択行はurlで何行目? そんなちょっとした、しかしチームでソース共有時に手間を大幅に減らしてくれます。

いまだに使っていますが、そろそろ標準であってもいいのでは..。

neue cc - Open on GitHub - Visual StudioからGitHubのページを開くVS拡張

marketplace.visualstudio.com

例えば行を選択した状態で、ブランチなり master を選ぶと?

url 上ですでに選択されています。便利!(微妙にいつも一行ずれます。)

SwitchStartupProject for VS 2017

Start up Project って設定面倒じゃないですか?右クリック嫌いだし目に見えてていいと思っています。

この拡張を使うとツールバーから Start up Project を確認、設定できます。ビルド同様ここにあった方が楽です。

marketplace.visualstudio.com

便利じゃないですか?

他にも multi-project startup として、複数プロジェクトの同時起動も簡単に設定できます。

https://bitbucket.org/thirteen/switchstartupproject/src/tip/Configuration.md?fileviewer=file-view-default

VSColorOutput

Visual Studio なぜかデフォルトでは Output (出力) Window に表示された文字の色を設定できません。エラーなら赤にしたいとかありませんかねぇ?

そんなときにこの拡張をいれると幸せになります。

marketplace.visualstudio.com

例えば正常ビルド

例えばエラー

分かりやすいと思います。

設定も随意に

こんなこともできたり。

Custom Document Well

すべては縦タブです。

marketplace.visualstudio.com

Open UserSecrets

ASP.NET Core では、プロジェクト名を右クリックすると Mange User Secrets (ユーザーシークレットの管理) メニューが出て、ローカル開発用 secrets.json をVSで編集しやすくします。 しかし、.NET Core Console ではこの機能がなく、せっかくMicrosoft.Extensions.Configuration.UserSecrets nuget をいれて User Secrets を取り扱えるようにしても、VSであつかうのに困ります。

この拡張を使うとそんな苦しみがなくなります。

marketplace.visualstudio.com

BlackSpace

末尾の余計なスペースをハイライト表示してくれます。設定で保存時に自動的に消すこともできるので便利便利。

marketplace.visualstudio.com

Github 上で余計なスペース見かけるとちょっと嫌になる人におすすめ。Hard Tab / Soft Tab が見やすくなるというのもあったり。

LetMeEdit

VS2017 では、新csproj フォーマットにおいてVS標準で csproj の編集メニューが追加されたのでLetMeEditは不要です。ただし旧csprojではunloadなしの編集はサポートされていないのでLetMeEditがいいでしょう。

LetMeEditを使うと、プロジェクトファイルを unload することなく編集できるようになります。

VS2015 までは EditProj という拡張を使っていましたが、2017 対応していません。そんな人は LetMeEdit がそのままの使い心地を提供します。

marketplace.visualstudio.com

unload って手間だとわかります。ソースや.csproj の変更時に必ず reload しないと整合性おかしくなりやすいので注意です。(自動的に聞かれます)

PowerMode

楽しいは正義。ただし画面が大きかったりパーティクルを多くすると重いです。

https://liammorrow.gallerycdn.vsassets.io/extensions/liammorrow/powermode/2.2/1495174801744/264136/1/demo.gif

marketplace.visualstudio.com

画面のゆれを止めたりするとカクツキがへって快適に楽しくなります。わたしはこんな設定。

VS Code でみんな大好きアレのインスパイアですね。

atom.io

Fix Mixed Tab

私はソフトタブを使うのですが、まれに直接ファイル編集したときや何かのインポートで入ったファイルはハードタブだったりします。この混在ファイルに対して、untabify (ソフトタブ) か tabify(ハードタブ) を一括適用できる拡張です。

marketplace.visualstudio.com

SlowCheetah

以前記事にしたようなことをするには必須のパッケージです。なんというか便利極まりないです。ただ、シンプルなプロジェクト構成ならいらないかなぁとも思います。

tech.guitarrapc.com

marketplace.visualstudio.com

2018/12/17追記

ASP.NET Core で紹介された DI によるコンフィグ構成がサポートされたことで、ビルド時のConfiguration Transform はもう不要な感じもあります。ということで以前よりおススメさげました。(4 -> 2)

追記ここまで

Open Command Line

cmd を開いたり、パスコピーだったりできます。便利。

marketplace.visualstudio.com

ちなみにこいつの Open Project File は unload をするので嫌いです。

使わなくなった拡張

以前、Productivity Power Tools 2017/2019 - Visual Studio Marketplaceを挙げていましたが、VS2017から機能は個別にパッケージ提供されています。そのため、Productivity Power Tool自体は使っておらず個別の機能をインストールしています。

利用している個別パッケージはリストに書いてある、Custom Document Well、Fix Mixed Tabsです。

Productivity Power Tools 2017

Visual Studio でタブの位置を横に持っていきたい、タブの色をプロジェクトごとに変更する、空白行をぺちゃんこにしたい、スクロールマップとソースコードの位置を合わせたい、Ctrl + Click で定義をピーク表示 などなど様々な「使う機能」が集約されています。

何気に Visual Studio Market Place 上のいくつかの Microsoft パッケージはこれに統合されています。

marketplace.visualstudio.com

一番うれしいのが Hard Tab と Soft Tab*2 が混じった時の統一ポップアップです。私は Soft Tab 好きなので Untabify ですねぇ。

まとめ

様々な拡張があります。なにげに VS2015 と VS2017 で引き継がれていない拡張も多いです。いい機会なので見直してだいぶん減らしました。

ReSharper も実は辞めました。Ctrl + T が VS2017 だと Ctrl + 1, Ctrl + T なんですね、今知りました (

http://visualstudioshortcuts.com/2017/visualstudioshortcuts.com

*1:VS2017 から起動に時間がかかるようになる原因のパッケージを教えてくれたりします。

*2:https://stackoverflow.com/questions/26350689/what-are-hard-and-soft-tabs

Github for Unity を導入してみる

Unity はいくつかの理由で Github.com を使うのが手間になることがあります。その中で最も障害となりえるのがlarge binary assets、つまり大きなサイズのバイナリファイルの扱いです。

今回は、先日発表された Github for Unity を利用することで、このフローがどう改善するのか見てみましょう。

github.com

目次

なぜ Github.com で Unity を管理するのが手間なのか

さて、ゲーム開発は多くのスタッフが協力して成り立ちます。コードやアセットの管理にGithub のような 分散バージョン管理システム(DVCS) を用いるのは、エンジニアから見るとごく自然だと思います。

これはUnity のようなゲームエンジンでコンテンツを作るときも同じです。しかし Unity をはじめとしてゲーム開発ではアセットが大量に必要になります。特に Texture *1 ファイルの数とサイズが大きくなりやすいことで苦しいことも多いのではないでしょうか。

ここで立ちはだかるのがGithub.com のDisk Quota と称される単一ファイル、リポジトリへの制限です。*2

About large files on GitHub - GitHub Docs

2017/7/14時点で次の推奨 (事実上の制限) になっています。*3

  1. We recommend repositories be kept under 1GB each. This limit is easy to stay within if large files are kept out of the repository. If your repository exceeds 1GB, you might receive a polite email from GitHub Support requesting that you reduce the size of the repository to bring it back down. (リポジトリサイズ : 1GB未満維持を推奨。超えた場合サポートからのサイズ縮小リクエストが来ます。)
  2. we place a strict limit of files exceeding 100 MB in size (単一ファイル当り 100MBの制約)

※ 他にも Webからのファイル追加では、25MBまでのファイルサイズ制限があったりしますが主眼ではないので割愛します。

このGithub.com サイズ制約を意識していないと、大量の画像ファイルやメッシュデータなどをバージョン管理しようとしたときに容易に制限に引っかかってしまいます。私は当時、よくpush しようとして File limit でpushが失敗してしょんぼりしましたが皆さんはどうでしょうか?

大容量に成りうるファイルのバージョン管理には Git LFS を利用する

Github は、そういった大容量のファイルを扱う際は Git LFS を利用することを推奨しています。

Managing large files - GitHub Docs

git-lfs.github.com

LFS 対象のファイルは、Github 上ではポインタファイルとして扱われるのでファイルの大きさによらず 1KB未満 で済みます。やったね。

version https://git-lfs.github.com/spec/v1
oid sha256:4665a5ea423c2713d436b5ee50593a9640e0018c1550b5a0002f74190d6caea8
size 36

おそらく初めに悩むのが、git LFS が利用している git の smudge filterclean filter 機能だった方は私だけではないのではないでしょうか?実際、このfilter でトラブルが起こったります。*4

Git LFS に関しては素晴らしいQiitaエントリがあるので参考にどうぞ。smudge/clean filterについてもサクッと理解できます。

qiita.com

ただ git と git lfs は別のコマンドなので少し面倒な部分があります。またコマンドはなく何かしらのGit を扱えるツールを用いる場合、別途 Git LFS をサポートしている必要があります。

Github for Unity を用いないこれまでの流れ

さて、Github + git lfs の例を見てみましょう。

今回はサバイバルシューターのチュートリアルでやってみます。

unity3d.com

ディレクトリを見ると Textures に pngや tif があるのでこれを git lfs 対象としてみます。

まず、.gitattributes はこのようにしてみました。

gist.github.com

私はUnityの場合、.gitignore をこのようにしています。

gist.github.com

早速 git add します。Source Tree で staging にあげようとすると2MBを超えるファイルは警告がでますね。もちろんこの *.asset も git lfs の対象拡張子にしています。

さて、git add でstaging エリアに上がったファイルを見ると smudge フィルタを通ってポインタファイル化されています。例えば、ArchesDiffuse.png という画像(バイナリ)ファイルがテキストで表現されています。

SourceTree で push 状態を見ると Git LFS に対象ファイルがどんどんpushされているのが見えます。これはもちろんコマンドでも同様にわかります。

無事に普段と同じフローで lfs 対象ファイルもpush されました。

では Github.com 上のファイルを見てみます。

github.com

先ほどの ArchesDiffuse.png は、中身がポインタファイルにも関わらず、Github.com 上ではちゃんと png としてLFS先の実体画像が表示されています。LFS対象でない普通の画像ファイルと同じように扱えているのは素晴らしいですね。

GithubForUnitySample/ArchesDiffuse.png at master · guitarrapc/GithubForUnitySample · GitHub

通常のファイルと LFS 管理ファイルの違いは、Stored with Git LFS という表記があることです。

では実際の中身を見るため、Rawファイルを見てみましょう。

https://raw.githubusercontent.com/guitarrapc/GithubForUnitySample/master/Assets/Textures/ArchesDiffuse.png

先ほど同様ポインタファイルになっていることが分かります。

git lfs の場合の git lfs clone/git lfs pullはここでは主眼ではないので、ぜひ試してみてください。

Github for Unity を用いてみる

さて通常の git/git lfs を用いた Github での Unity ファイルのバージョン管理はわかりました。これが Github for Unity でどう変わるか見てみましょう。

ダウンロード

導入するためには、.unitypackage を使います。つまり、プロジェクトごとに利用を選択できます。

GitHub for Unity | Bring Git and GitHub into Unity

パッケージは、リリースタグから取得できます。今のところは Alpha なので動作が不安定なことは理解した上で使うといいでしょう。*5

Releases · github-for-unity/Unity · GitHub

今回は、 v0.15-alpha を用います。

事前準備とかは、README を見るといいです。ブログ見る必要なくなります。

Unity/README.md at master · github-for-unity/Unity · GitHub

なお、現在 サポートしている Unity は 5.4~5.6。LFS は 2.xです。

  • Unity 5.4-5.6 (Unity Personal も可)
    • We've only tested the extension so far on Unity 5.4 to 5.6. There's currently an blocker issue opened for 5.3 support, so we know it doesn't run there. There are some issues for 2017.x, so it may or may not run well on that version. Personal edition is fine
  • Git and Git LFS 2.x

Unity へのパッケージ導入

ツールバーから行きます。 Assets > Import Package > Custom Package.... からダウンロードしたgithub-for-unity-0.15.0.0-alpha.unitypackage を導入します。

いい感じに導入されました。

ツールバーに Github メニューが増えました。

Github ログイン

ツールバーのGithub > Authenticate からGithub にログインできます。このアカウントは、Github Web にログインするときの UserName/Password を用います。

注意点として、内部実装として、Octokit.Net を用いて Github API v3 を使っているため、2FA を使っていても Password です。通常2段階認証の場合、Password の代わりに Personal Access Token というケースが多いと思いますが違うので注意してください。

2段階認証を使っている場合は、2FA PIN コードが求められます。

上手く認証できましたか?

Github ウィンドウ

ツールバーから Github > Show Window でGithub のタブが開きます。

さて、5つのタブがあります。

  • Changes
  • History
  • Branches
  • Settings
  • Account

History タブ

ここにはコミットグラフが表示されます。

先ほどの initial commit というコミットログがグラフに表示されています。

またここから、Getch / Pull / Push 操作が可能です。

コミットログを選択すると対象のファイルがツリー表示されます。

残念なことにツリービューのみの提供に加えて、ここのウィンドウサイズは変更できないため非常に見にくいです。

なお現在は日本語が文字化けます。PRがきているようですが....サイドエフェクトがあるようなのでいつになるか待っている状況です。

github.com

github.com

github.com

Branches タブ

local と remote のブランチ状況が表示されます。

もちろんここでリモートブランチに切り替えたり、新規でブランチを切ることもできます。

ブランチをダブルクリックして checkout できます。

Branches の上に現在のブランチが表示されています。ブランチ一覧でも微妙に bold になっています。

Settings タブ

Git 設定状態や Lockされたファイル一覧が見えます。

Locked ファイルは、噂の他の人との共同作業用の機能です。わくわく機能ですね。

You can see who has locked a file and release file locks after you've pushed your work. https://github.com/github-for-unity/Unity#settings-tab

さて、Path to Git を見るとここで使っている Git が Portabl Git であることが分かります。

C:\Users\<UserName>\AppData\Local\GitHubUnity\PortableGit_f02737a78695063deace08e96d5042710d3e32db\cmd\git.exe

Account タブ

Go to Profie で、Github.com 上のプロファイルページに移動します。

Changes タブ

ここに、git 上の変更が表示されます。ファイルの変更は色で表現されています。

  • 追加 : 緑
  • 削除 : 赤
  • 変更 : 黒
  • ファイルの移動 : 削除と追加として表示されます

残念ながらこの画面で可能なのは、Commit のみです。

よくあるあるな、Discard/Remove/Reset/Ignore などの操作をしようにも右クリックが出ないため git 操作の完結をするには機能不足です。

残念ながら File Diff もサポートしていませんが、Issue に上がって修正PRがマージ済みです。

t.co

github.com

また Staging Area もありませんが、これはIssueが上がっています。

github.com

Commit 操作

Changes タブからGithub for Unity を取り込んだコミットをしてみます。チェックをつけて Commit メッセージを書きます。

これで Commit してみましょう。

上手くいきました。

これを push します。ここが少し時間かかります。が、問題なく リモートブランチを作ってプッシュされるのでちょっと待ってみてください。

push したら、Github.com 上で LFS 対象ファイルがどうなったか見てみましょう。うまく扱えているでしょうか?

https://github.com/guitarrapc/GithubForUnitySample/blob/master/Assets/Editor/GitHub/GitHub.Api.dll

問題なく push されたようです。

マージしてみましょう。残念ながら Github for Unity から PRを直接作成できないので Github.com 上からPRを作ります。

PR をマージします。

Github for Unity で Branches タブから master を checkout します。で、当然 Github for Unity が入る前になるのでタブ消えます。(あっ)

ちなみにSource Tree 上ではこのように表示されるのでorigin/master を Pullしておきます。

さて History を見ると、微妙にツリーっぽい表示になっています。

SourceTree などは見やすいツリー表示なので、ちょっとこれだけで把握は難しいですね。

これで一通りの動きが見えました。

コントリビュート

さて、Github for Unity は Github 上で公開されています。Issue を積極的に受け入れる姿勢を見せており、Twitter でつぶやくと補足されます。うれしいです。

ということで、改善点があれば Issue で積極的に共有するといいでしょう。コードはC# で書かれており、PR も積極的に受け付けているので、ぜひみなさんもどうでしょうか。

私からは4つIssue をあげてあります。

  • Index.lock が消えないケースがあること

github.com

  • Tree View だけでなく、Flat List View を用意することの提案

github.com

  • Changes タブでの Remove/Discard/Reset/Ignore などの git のフル操作の提供提案

github.com

  • Github for Unity から直接PRをする提案

github.com

まとめ

Github for Unity、まだUnity のみですべての git 操作が完結するというには早いです。しかしアルファのわりに動作は比較的安定しており、ある程度の層がここからできます。

ぜひ使ってみるといいと思います。

参考

Github for Unity の基本的な使い方は他の方もまとめておいでです。

http://stepup-next.halfmoon.jp/blog/2017/06/30/unity-for-github/stepup-next.halfmoon.jp

*1:tga や png などの画像ファイル

*2:Github Enterprise のようなホスティングは別とします。またほかのgit を使ったサービスも記事の対象外です。

*3:このサイズ制限がなければ、大規模なUnity開発でも懸念をいただくことなくGithub.com 利用できるですが仕方ありません。

*4:ちなみにlfsの対象指定は smudge/clean filter の指定 = .gitattributes で行います。 https://git-scm.com/book/en/v2/Customizing-Git-Git-Attributes

*5:αってそういう意味だしそこを割り切ると楽しく使えます。