NuGet

パッケージを作成して NuGet で公開する

Clark Sell

昨年の 11 月号で、開発者向けの新しいパッケージ管理エコシステムとして NuGet を Phil Haack が紹介しました (msdn.microsoft.com/magazine/hh547106)。NuGet は Outercurve Foundation のプロジェクトで、Microsoft .NET Framework の最も優れたパッケージ管理システムとなることを目指しています。プロジェクト チームは、開発者コミュニティと連携して活動しているマイクロソフトの開発者が大半を占めます。開発エコシステムに NuGet を導入すれば、.NET 開発者がパッケージを使用、作成、および公開できるようになります。

一見、NuGet はオープン ソース コミュニティのみを対象としたツールに思えるかもしれませんが、その役割はほんの一部にすぎません。NuGet は、オープン ソース コミュニティ内でパッケージを配布するだけでなく、企業がファイアウォールの内側で社内パッケージを提供することにも目指して設計されています。つまり、NuGet が扱う範囲は幅広く、マイクロソフト、オープン ソース コミュニティ全体、および固有の社内サーバーからパッケージをインストールおよび更新することができます。

今回は、NuGet のパッケージ作成者になるために必要なことについて扱います。NuGet は、開発ライフサイクルに簡単に組み込むことができ、たいへん大きなメリットをもたらします。NuGet を導入したら、パッケージの使用、作成、および配布にかかわる問題に悩んでいたころを懐かしく思うことでしょう。来月は、自身のパッケージをホストするために必要なことを取り上げる予定です。

エコシステムの定義

パッケージ作成者になる方法を説明する前に、大きなエコシステムについて簡潔にまとめます。NuGet は、パッケージ配布者の観点からはいくつか重要なコンポーネントで構成されていて、主なものに NuGet.exe というコマンドライン ユーティリティ (nuget.codeplex.com/releases/view/58939、英語) と、パッケージをホストするサーバー(公式の NuGet ギャラリーである nuget.org など) があります。前回説明したように、NuGet へのアクセス方法には、NuGet.exe、Visual Studio ([表示] メニューの [その他のウィンドウ])、および NuGet Package Explorer (npe.codeplex.com、英語) の 3 つがあります。これらのユーティリティは、1 つまたは複数の NuGet リポジトリを操作します。それに対し、nuget.org (英語) は、NuGet パッケージを保存、ホスト、および公開するパブリック ギャラリーです。nuget.org は、NuGetGallery (github.com/nuget/nugetgallery、英語) という別のオープン ソース プロジェクトを使用して構築されています。次回は、独自の NuGet ギャラリーをホストする方法を詳しく説明する予定です。

パッケージ作成者は、NuGet リポジトリにさまざまなパッケージを公開できます。パッケージには複数のバージョンを含めることが可能です。nuget.org では、ユーザーがパッケージについての詳細を読んだり、パッケージをインストールしたり、パッケージの所有者に連絡を取ったり、(まれなケースではありますが) 不正利用について報告したりできます。

パッケージ作成者は、バージョン番号、依存関係、パッケージのインストール方法などの項目を管理することができます。

セットアップする

パッケージを公開するには、nuget.org をリポジトリとして使用している場合、NuGet ギャラリーのアカウントにサインアップする必要があります。作成者になるのは簡単です。nuget.org (英語) で [Upload Package] (パッケージのアップロード) をクリックします。[Register now] (今すぐ登録) をクリックして登録フォームを表示し、必要な情報を入力して新しいアカウントを作成します。すると、nuget.org から、電子メール アドレスとアカウントを確認するための URL が記載された電子メールが送信されます。

アカウントを確認したら、サイトにログインしてアクセス キーを取得します。アクセス キーとは、一意のトークンです。nuget.org リポジトリに自身を認識させる役割があるほか、パッケージ更新の公開などのさまざまなパッケージ管理タスクを自動化できるようにします。

今回は、NuGet.exe コマンド ラインと、NuGet Package Explorer の両方を紹介します。コマンド ライン バージョンをダウンロードしたら、そのパスを含めるようにシステムの Path 環境変数を更新することをお勧めします。そうすると、システムのあらゆる場所から NuGet を簡単に使えるようになります。

パッケージのしくみ

前回触れたように、NuGet パッケージは、ファイル拡張子に .nupkg を付けた、Open Packaging Convention (OPC) コンテナー ファイルです。パッケージの形式は、規則に大きく依存しており、ルートには nuspec というマニフェスト ファイルがあります。以下に、ディレクトリ構造の例を示します。最初は次のようになっているはずです。

Root Folder
|   package.manifest
+---lib
+---content
+---tools

ルートには以下 3 つのフォルダーがあります。

  • lib: 参照するすべてのアセンブリを保持
  • content: ターゲット プロジェクトのルートにコピーされるファイルとディレクトリを保持
  • tools: パッケージのインストール時や、ターゲット プロジェクトが Visual Studio に読み込まれるたびに実行される、カスタムの Windows PowerShell スクリプトを保持

このうち、lib フォルダーが最も複雑です。このフォルダーには、次のように、フレームワークの依存関係に対応したサブフォルダーを含みます。

Root
| package.manifest
\---lib
    | MyFirstAssembly.dll
    \---net11
        | MySecondAssembly.dll
    \---net20
         | MySecondAssembly.dll
           \---sl
        | MySecondAssembly.dll
    \---netmf
         | MySecondAssembly.dll
+---content
+---tools

(これはめったにないことですが) アセンブリがすべてのバージョンの .NET Framework で機能させる場合、アセンブリは、MyFirstAssembly.dll のように、lib フォルダーのルートに含めるだけでかまいません。

ですが、このシナリオが非常にまれであることを踏まえ、NuGet チームはこの手法を採用しないことを強く勧めています。アセンブリは、特定のバージョンのフレームワークに依存させることを推奨しています。そのため、フレームワークのフォルダーをバージョンごとに用意して、このフォルダーに適切なバージョンのアセンブリを含めます。例のフォルダーからわかるように、MySecondAssembly.dll のバージョンには、.NET Framework 1.1、.NET Framework 2.0、Silverlight、および .NET MicroFramework にそれぞれ対応したバージョンがあります。これにより、NuGet で、ターゲット フレームワークに適したパッケージがインストールされるようになります。

ユーザーがパッケージをインストールする際、NuGet は、プロジェクトのターゲット フレームワークに基づいて適切なアセンブリをインストールします。前の例で、ユーザーが .NET Framework 4.0 をターゲットとするプロジェクトにパッケージをインストールするとしたらどうなるでしょう。例の lib フォルダーには、フレームワークとして .NET Framework 4.0 が存在しないため、NuGet は最も近いバージョンのフレームワークを選んで使用します。例では、net20 フォルダーにあるアセンブリが使用されます。

content フォルダーは、ターゲット プロジェクトのルート フォルダーの複製です。このフォルダーにあるものはすべて、ターゲット プロジェクトにそのままコピーされます。たとえば、ターゲットの /images フォルダーに画像をコピーする場合、それらの画像を /content/images フォルダーに格納します。

tools フォルダーには、パッケージのインストール中、プロジェクトの起動時、またはユーザーの使用時に NuGet が呼び出す Windows PowerShell スクリプトをすべて含めます。このフォルダーは、ターゲット プロジェクトにコピーされると、Visual Studio パッケージ マネージャー コンソール内の `$env:Path (PATH) 環境変数に追加されます。

NuGet には、files ノードという、パッケージの設定を自動化する組み込み機能があります。files ノードでは、.nupkg ファイルの作成時に、パッケージ構造にコピーする必要があるファイルを明示的に指定できます。これは、パッケージ化のプロセス全体を自動化するのに役立ちます。file 要素は単純で、src 属性、target 属性、および exclude 属性を定義します。ご想像のとおり、src はコピーするファイルを、target はコピー先を、exclude はコピーしないものを定義します。以下に例を示します。

<files>
  <file src="bin\Debug\*.dll" target="lib" />
  <file src="bin\Debug\*.pdb" target="lib" />
  <file src="tools\**\*.*" exclude="*.log" />
</files>

パッケージを作成する別のプロセスがある場合、.nuspec ファイルの files ノードは無視してかまいません。

.nuspec ファイル

nuspec ファイルは、パッケージ マニフェストです。これは、パッケージ全体を定義する単純な XML ファイルで、名前、バージョン番号、パッケージ参照などを含みます。新しいマニフェストを作成するためには、開始点として使える NuGet.exe の spec というコマンドを使用します。コマンドは次のとおりです。

> NuGet.exe spec

spec コマンドは、サンプル データを含む有効なファイルである package.nuspec という新しいファイルを作成します。図 1 に、spec によって作成されるファイルの例を示します。

図 1 .nuspec ファイルの例

<?xml version="1.0"?>
  <package >
    <metadata>
      <id>Package</id>
      <version>1.0</version>
      <authors>csell5</authors>
      <owners>csell5</owners>
      <licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl>
      <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl>
      <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>
      <requireLicenseAcceptance>false</requireLicenseAcceptance>
      <description>Package description</description>
      <copyright>Copyright 2011</copyright>
      <tags>Tag1 Tag2</tags>
      <dependencies>
        <dependency id="SampleDependency" version="1.0" />
      </dependencies>
    </metadata>
  </package>

このファイルを作成したら、サンプルの値を置き換えます。1 回置き換えるだけでよい値がほとんどですが、何度か置き換えなければならない値もあります。たとえば、パッケージの ID は公開後変更すべきではありませんが、バージョン番号はリリースごとに変わります。

nuget.exe spec は、Visual Studio プロジェクト ファイル (.csproj や .vbproj など) に対して実行することもできます。この場合、既定値はプロジェクト ファイルのメタデータに基づいてあらかじめ設定されます。以下に、.nuspec ファイルの簡単な要素をいくつか示します。

  • id: パッケージの一意識別子
  • title: パッケージのわかりやすいタイトル
  • description: パッケージの詳細な説明
  • summary: パッケージの簡単な説明
  • licenseURl: ライセンスへのリンク
  • copyrights: パッケージの著作権についての詳細

最上位の要素は現在 28 個あります。nuspec ファイルの内容は非常にわかりやすくなっていますが、詳細について知りたい方は bit.ly/lgQ4J4 (英語) を参照してください。今度は、複雑な .nuspec の要素についていくつか見てみましょう。

依存関係と参照

依存関係の管理にはだれもが苦労します。依存関係のチェーンが長くなり、もつれ合う場合はなおさらです。たとえば、PackageA を構築したとしましょう。このパッケージが、同じく NuGet に存在している PackageB を使用することになりました。この場合、PackageB をパッケージに含めるのではなく、そこに "依存関係" を作成するだけでかまいません。ユーザーが PackageA のインストールを開始すると、NuGet はまず、その依存関係について nuspec ファイルを調査します。その後、依存関係をもれなく満たすためにダウンロードする必要があるすべてのパッケージのグラフを作成し終わるまで、依存関係のあるパッケージを 1 つ 1 つ確認し、その依存関係などを調査します。それから、グラフ全体のパッケージをダウンロードして、インストールします。この NuGet の機能は、パッケージの作成とインストールを大幅に簡略化します。

以下の依存関係ノードで、定義されているパッケージの依存関係をいくつか見てみましょう。

<package>
<metadata>
<dependencies>
<dependency id="SampleDependency" version="1.0" />
  <dependency id="AnotherSampleDependency" version="[1.2,2.5)" />
</dependencies>
</metadata>
</package>

依存関係は、いくつでも指定することができます。どの場合でも、id 属性は依存関係のあるパッケージを示し、version 属性は必要なバージョンの範囲を表します。例では、バージョン 1.0 以降の SampleDependency プロジェクトとの依存関係が示されています。

NuGet のバージョンの範囲指定によって、許可するバージョンの範囲を設定できます。たとえば、version="[1.2, 2.5)" のように設定します。角かっこはそのバージョンを含むことを定義し、丸かっこは含まないことを定義します。この例では、バージョンが 1.2 以上かつ 2.5 未満のパッケージが容認されます。NuGet は、この範囲の中で最も新しいバージョンを使用します。バージョン範囲指定の詳細については、bit.ly/qVXWxs (英語) を参照してください。

場合によっては、パッケージをインストールするユーザーが、.NET Framework アセンブリの型に対してプログラミングを行う必要があります。適切な参照を追加するには、.nuspec ファイルに frameworkAssemblies ノードを追加して、必要なフレームワークのアセンブリを指定します。以下に例を示します。

<package>
  <metadata>
    <frameworkAssemblies>
      <frameworkAssembly assemblyName="System.Something" targetFramework="net40" />
      <frameworkAssembly assemblyName="System.SomethingElse" />
    </frameworkAssemblies>
  </metadata>
</package>

変換

ほとんどのプロジェクトは、アセンブリ参照だけでは適切に機能しません。機能するためには、.config ファイルやソース コードの変更が必要になる場合がありますが、NuGet はどちらのシナリオもネイティブにサポートします。ここでは、.config ファイルの変換に注目します。変換の詳細については、bit.ly/jqzry2 (英語) を参照してください。

NuGet は、NuGet パッケージのインストール中、新しい .config 値を追加するための変換を行います。NuGet がこの変換を行うようにするには、パッケージの content フォルダーに変換ファイルを追加する必要があります。これは transformation という拡張子を付けた有効な XML ファイルで、ファイル名は変換を適用するファイルと同じにします。たとえば、web.config ファイルに変換を適用するためには、web.config.transformation というファイルを追加します。

変換ファイルには、ターゲット ファイルに追加する必要がある構成ファイルのセクションのみを含めます。たとえば、ユーザーの system.webServer セクションに新しいモジュールを追加する場合は、次のように、変換ファイルにセクション全体を追加します。

<configuration>
  <system.webServer>
    <modules>
      <add name="NewModule" type="My.NewModule" />
    </modules>
  <system.webServer>
</configuration>

NuGet は、追加するセクションで既存のセクションを置き換えるのではなく、それらをマージします。したがって、固有のモジュールが含まれているモジュール セクションが既にターゲット ファイルに存在している場合、インストール後にマージされる 2 つのファイルは次のようになります。

<configuration>
  <system.webServer>
    <modules>
      <add name="ExistingModule" type="Their.ExistingModule" />
      <add name="NewModule" type="My.NewModule" />
    </modules>
  <system.webServer>
</configuration>

ご覧のとおり、モジュールは、既存のモジュールの最後に追加されます。ユーザーがパッケージを削除する場合は、(セクションに変更を加えていない場合は) 変更部分のみが削除され、残りの部分は最初の位置に残ります。

バージョニング

バージョニングは、構築するあらゆる点で重要な位置を占めます。NuGet パッケージのバージョンはそのパッケージを指しており、必ずしもそのパッケージに含まれるアセンブリについて示すわけではありません (ただし、一致させるのが一般的です)。.nuspec ファイルでは、パッケージのバージョン番号を、以下のように N.N.N.N の形式で定義します。

<package>
  <metadata>
    <version>1.2.3.4</version>
  </metadata>
</package>

.nuspec ファイルには、静的文字列だけでなく置換トークンも使用できるプロパティがいくつかあります。バージョン要素も、このうちの 1 つです。したがって、1.2.3.4 のように静的文字列を定義する代わりに、[$version$] というトークンを挿入してもかまいません。これは、後に NuGet.exe によって置換されます。このトークンがあると、アセンブリの AssemblyVersionAttribute で指定されたバージョンが、次のように .nuspec ファイルでも指定されます。

<package>
  <metadata>
    <version>$version$</version>
  </metadata>
</package>

このオプションは、パッケージとバージョンを一致させる場合に便利ですが、推奨されない理由もたくさんあります。

パッケージ化する

前述したように、NuGet パッケージは、.nupkg というファイル拡張子の付いた OPC ファイルです。コマンド ラインからパッケージを作成するには、次のように pack コマンドで NuGet.exe を呼び出し、.nuspec ファイルに渡します。

> NuGet.exe Pack YourPackage.nuspec

spec と同様、プロジェクト ファイルに対して pack を実行することもできます。NuGet は、.csproj ファイルか .vbproj ファイルにあるメタデータのみに基づいて完全な NuGet パッケージ (.nupkg ファイル) を構築します。既に .nuspec ファイルを作成している場合、pack はその .nuspec ファイルを使用します。以下に例を示します。

> NuGet.exe pack [path]\MyProject.csproj

これで、初めての NuGet パッケージの完成です。

シンボルのサポート

Visual Studio には、開発者が必要に応じて行単位でソース コードを実行できるすばらしい機能があります。NuGet には、シンボル パッケージを作成および公開する機能があるため、これに対応しています。シンボル パッケージを作成するには、次のように、pack を使用する際に –symbols オプションを使用します。

> NuGet.exe pack MyProject.nuspec -symbols
> NuGet.exe pack MyProject.csproj –symbols

pack は、MyProject.nupkg と MyProject.Symbols.nupkg の 2 つの .nupkg パッケージを生成します。.symbols.nupkg は、NuGet.exe push というコマンドを使用すれば SymbolSource.org に公開することができます。NuGet でのシンボル パッケージの作成の詳細については、bit.ly/jqzry2 (英語) を参照してください。

NuGet.org への公開

パッケージを作成したら、さっそく公開しましょう。push は、サーバーにパッケージを公開するための NuGet のコマンドで、最新のソース管理システムのように機能します。これまで扱ったコマンドとは異なり、push は以下のように多くの引数を受け取ります。

> NuGet.exe push <package path> [API key] [options]
  • package path: パッケージへのパス
    例: c:\MyPackge\MyPackage.1.0.nupkg
  • API key: 一意のアクセス トークン
    例: ABFC2E12-40B3-41A1-A7CC-8FC9AB3A71E0
    これは省略可能です。設定するには NuGet.exe setApiKey コマンドを使用します。
  • -source (src): パッケージを公開するサーバー
    例: -source http://packages.nuget.org/v1/
    これは、他の場所に公開する場合を除き、省略可能です。
  • -CreateOnly (co): パッケージを作成してギャラリーにアップロードするが、公開しない
    これは省略可能で、既定では false です。

次のコマンド例は、MyPackage パッケージを NuGet に公開します。

> NuGet.exe push MyPackage.1.0.nupkg ABFC2E12-40B3-41A1-A7CC-8FC9AB3A71E0

図 2 のように、NuGet の Package Explorer も使用できます。

Publishing with the NuGet Package Explorer
図 2 NuGet の Package Explorer を使用した公開

シンボル パッケージを構築した場合、NuGet はそれを自動的に検出し、nuget.org と symbolsource.org の両方のリポジトリに公開します。シンボルのソースとして symbolsource.org を使用するようにターゲット コンピューターがセットアップされている場合、開発者は、必要に応じて Visual Studio でパッケージのソース ファイルにステップ インすることができます。

これで公開が完了です。公開したのがバージョン 2 のパッケージの場合、このバージョンが既定になります。前回説明したように、ユーザーがパッケージの更新について検索すると、パッケージが更新されていることが表示されます。

まとめ

おそらく、開発チームにはなんらかのビルド プロセスや配置プロセスがあると思います。私と考えが同じ方は、NuGet をそのプロセスに組み込む方法について考え始めているのではないでしょうか。ここで紹介したコマンドは、もちろんビルド プロセスに取り入れることができますが、Team Foundation Server (TFS) を使用している場合はもっと簡単な方法があります。

TFS NuGetter (nugetter.codeplex.com、英語) は、TFS 2010 用にビルド プロセスを拡張するオープン ソースのプロジェクトで、NuGet を中心に据えながら、あらゆる必要なバージョニング、パッケージング、配置を、カスタマイズおよび反復可能な方法で実行します。パッケージの公開先がどこであっても、TFS NuGetter は大幅に時間を節約します。

NuGet は、この業界では新しい概念ではありませんが、.NET 開発者にとっては革新的なシステムです。切望されていたパッケージ管理機能を提供する NuGet は、開発を本職としない方から大企業にお勤めの方まで、あらゆる開発者に役立ちます。パッケージを公開できるだけでなく、ユーザーに自分の作業を発見してもらうこともできます。NuGet にパッケージを公開して、ぜひ自分の成果を披露しましょう。

ここで使用したリンクはすべて、on.csell.net/BeANuGetAuthor (英語) にまとめています。

Clark Sell は、シカゴ郊外でマイクロソフトのシニア Web エバンジェリストとして活躍しています。彼のブログは csell.net (英語) で、ポッドキャストは DeveloperSmackdown.com (英語) で公開されており、Twitter は twitter.com/csell5 (英語) からアクセスできます。

この記事のレビューに協力してくれた技術スタッフの David EbboPhil HaackMark Nichols、および Brandon Satrom に心より感謝いたします。