2022-02-12

PoweShell Gallery へ公開するためのステップ覚書

タイトルの通り。これは未来の自分へのバトンだ。

毎回 PoweShell Gallery への公開方法を忘れたりする。 弊ブログのpowershellgalleryタグを参照すればどれだけ同じミスを繰り返してるかアホさがわかる。 加えて microsoft/PowerShellForGitHub で repo を作るのもしょっちゅう忘れる。 こちらに関してはもうそろそろ ghに乗り換えた方がいいのかも知れん。(けど、そうすると PowerShell の旨味であるオブジェクトでゴニョゴニョやりやすい世界がなくなってしまうのは困る)

なのでこれらを定型化して psake タスクに落とし込む等したいな~と考えている(今度はそのpsakeタスクが秘伝のソース化するかも知れんがそれはそれ)。 そのためにいつも何をやっているかを以下にリストアップする。


前提。

Step 1. repo とモジュールマニフェストの作成

まず最初のステップはモジュールの雛形作成。

# create repo.
$owner = 'krymtkts'
$module = 'PSMFAttendance'
New-GitHubRepository -RepositoryName $module -Private -LicenseTemplate MIT
ghq get -p (Get-GitHubRepository -OwnerName krymtkts -RepositoryName $module | Select-Object -ExpandProperty ssh_url)
cd "$(ghq root)/$(ghq list $module)"

# create module manifest.
mkdir $module
$author = 'Takatoshi Kuriyama'
New-ModuleManifest -Path "./$module/$module.psd1" -ModuleVersion 1.0 -Author $author -Copyright "(c) $((get-date).Year) $author. All rights reserved."

PowerShell Gallery への公開コマンド Publish-Module (PowerShellGet) は、指定したディレクトリの中身を全部 PowerShell Gallery にぶち上げるため、モジュール以外のファイルを配置していても全て雲の上に持っていかれてしまう(除外設定がないのだ)。 そういう事故を起こさないためにも、モジュールリリース用のディレクトリを作成することをおすすめする。 そこでモジュールを開発するか、リリース対象のコードをそこにコピーした上で公開する、というのに限るのではないだろうか。わたしはコピーしたあとの掃除とか考えるのが面倒なので前者。

先にモジュールマニフェストを作るのは、動作確認なんかで関数がエクスポートできているか見るのに使うからだ。

Step 2. モジュールマニフェストの更新

次は実装して、マニフェストの更新。 マニフェストに記すべき内容については公式のドキュメントを読むのが良い。

Creating and publishing an item - PowerShell | Microsoft Docs

とはいえわたしは大したモジュールを作らないのもあり、いつも更新するのは限られたフィールドだけだ。主に以下。

  • Description
  • PowerShellVersion
  • *ToExport
  • PrivateData.PSData.Tags
  • PrivateData.PSData.LicenseUri
  • PrivateData.PSData.ProjectUri

この中でわかりにくいのが、互換性のあるプラットフォームの指定。これは Tags で表す。 PowerShell Gallery Publishing Guidelines and Best Practices - PowerShell | Microsoft Docs

マニフェストの更新は手で書いてもよいが、Update-ModuleManifest も使える。 ただし New-ModuleManifestUpdate-ModuleManifest で出力結果のフォーマットが異なる(後者はインデントされない)のがイライラするので、手でやることが多いか。 ただ自動化していくとすれば、ここは Update-ModuleManifest に従うところか(こいつインデントしてくれへんのだが)。

Last Step. PowerShell Gallery 公開

最後は PowerShell Gallery への公開だ。

PowerShell Gallery への公開を実施する前に、API キーの期限が切れていないか必ずチェックしておく。 キーの有効期限が切れている場合のエラーが非常にわかりにくいので、無駄にトラシューに時間を費やさずに済ますためにも公開前にチェック兼ねて毎回キーを更新するのが妥当では? API キーの有効期限は最長 1 年しかないので、しょっちゅう切らしている。むしろ公開前に更新するフローであれば、期限も最短にできるのでよりセキュアかも知れない。

API キー はモジュール名に対して glob パターンでスコープを切れる。過去には面倒を回避するために*パターンを使ったりしていたが、今はパッケージ名の完全一致を利用していて、パッケージ毎に API キーを分けるようにしている。

公開の手順は krymtkts/Get-GzipContentを公開したときスクリプトを使う。

このスクリプトでは PSScriptAnalyzer を使ったチェックが成功した後に公開する。 また、WhatIf は Dry Run として置き換えた上で使うようにしていた。 WhatIf を使わないことで覚えることが増えて面倒な気もするが、とにかく間違って公開すると面倒なので、初期値を Dry Run にしたいという意図だった(多分)。この辺は ShouldProcess の勉強をしたらより良い案があるかも知れん。

PowerShell Gallery でのバージョニングは基本的に Semantic Versioning なので、何かしらミスったらモジュールの非公開はできるけど、同じバージョンへの更新はできない。パッチバージョンを上げて再公開とかしかできない。これはミスったら恥ずかしいしやり直しがきかないから、 Dry Run しまくる。この「何かしらミスったら」を Pester とかで事前チェックできると良いのだろうけど。

Param (
[String]$ApiKey,
[ValidateSet('Publish', 'DryRun')]$Mode = 'DryRun'
)

$ModuleName = Get-ChildItem -File -Path ./ -Recurse -Name '*.psd1' | Split-Path -Parent
$ArtifactPath = ".\$ModuleName\"
Write-Host "Check modules under $ArtifactPath."

$report = Invoke-ScriptAnalyzer -Path "$ArtifactPath" -Recurse -Settings PSGallery
if ($report) {
Write-Host "Violation found."
$report
exit
}
Write-Host "Check passed."

switch ($Mode) {
'Publish' {
Write-Host "Publishing module: $ModuleName"
Publish-Module -Path $ArtifactPath -NugetAPIKey $ApiKey -Verbose
}
'DryRun' {
Write-Host "[DRY-RUN]Publishing module: $ModuleName"
Publish-Module -Path $ArtifactPath -NugetAPIKey $ApiKey -Verbose -WhatIf
}
}
if ($?) {
Write-Host 'Successfully published.'
}
else {
Write-Error 'Failed to publish.'
}

今改めて見るとこのスクリプト、API キーは Credential に変更した方がマトモだ。ぜひ対応したい。

このように使う。

.\publish.ps1 -Mode DryRun -ApiKey xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

.\publish.ps1 -Mode Publish -ApiKey xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

公開後は最終チェック、自端末へモジュールをインストールして一通り使えるか見ている。何しかちゃんと出来てるか不安。

公開できたら、 repo のREADME.md に以下を加筆する。これは流石に自動化無理なのでいいや。

  • PowerShell Gallery からのインストール方法
  • Shields.io の PowerShell Gallery のダウンロード

終。


おわりに

手順起こしてみてあれやけど、これもうすぐにでも psake タスク化できそう。単にサボってただけだったか...