NuGet

Как стать автором NuGet-пакетов

Кларк Селл

Продукты и технологии:

NuGet, Visual Studio, TFS NuGetter

В статье рассматриваются:

  • экосистема NuGet
  • анатомия пакета
  • манифест пакета nuspec
  • публикация на NuGet.org
  • автоматизация процесса сборки с помощью TFS NuGetter

В ноябрьском номере Фил Хаак (Phil Haack) представил NuGet, новую экосистему управления пакетами для разработчиков (msdn.microsoft.com/magazine/hh547106). NuGet — это проект Outercurve Foundation, который должен стать полноценной системой управления пакетами для Microsoft .NET Framework. Группа, работающая над проектом, состоит в основном из разработчиков Microsoft, которые трудятся в тесном взаимодействии с сообществом разработчиков. Введение NuGet в нашу экосистему разработки дает .NET-разработчикам возможность использовать, создавать и публиковать пакеты.

На первый взгляд NuGet может показаться инструментом лишь для сообщества, работающего над проектами с открытым исходным кодом, но это совсем не так. NuGet был специально создан не только для распространения пакетов в рамках сообщества сторонников открытого исходного кода, но и для доставки внутренних пакетов за брандмауэрами предприятий. Это означает, что вы можете использовать NuGet для установки и обновления пакетов от Microsoft, от сообщества сторонников открытого исходного кода и со своих внутренних серверов.

В этой статье мы изучим, что нужно для того, чтобы стать автором NuGet-пакетов. Включить NuGet в ваш жизненный цикл разработки не сложно, и это дает существенные преимущества. Как только вы это сделаете, ваши проблемы, связанные с использованием, созданием и распространением пакетов, станут далеким воспоминанием. В следующей статье я расскажу о том, что необходимо для хостинга ваших пакетов.

Определение экосистемы

Для начала позвольте кратко напомнить, что представляет собой более крупная экосистема. NuGet, с точки зрения дистрибутора пакетов, состоит из нескольких важных компонентов, главным образом из утилиты командной строки NuGet.exe (nuget.codeplex.com/releases/view/58939) и сервера для хостинга пакетов, например официального сайта NuGet Gallery (nuget.org). Вы можете взаимодействовать с NuGet тремя способами: используя NuGet.exe, применяя NuGet в среде Visual Studio (окно Package Manager Console, открываемое через View | Other Windows) и работая с NuGet Package Explorer (npe.codeplex.com). Эти утилиты взаимодействуют с одним или более репозитариями NuGet. С другой стороны, nuget.org — это общедоступная галерея, которую можно использовать для хранения, размещения и публикации NuGet-пакетов. Сайт nuget.org построен с применением другого проекта с открытым исходным кодом, известным под названием NuGetGallery, который вы найдете на github.com/nuget/nugetgallery. О хостинге собственной галереи NuGet я расскажу в следующей статье.

Как автор пакетов вы можете публиковать множество различных пакетов в репозитарии NuGet, и у каждого пакета может быть несколько версий. Nuget.org дает возможность своим клиентам читать подробные сведения о пакете, устанавливать его, связываться с владельцем пакета и в исключительных случаях сообщать о нарушении авторских прав. Вы также можете контролировать такие элементы, как номера версий, зависимости и то, каким образом устанавливается ваш пакет.

Подготовка

Для публикации пакета, если вы будете использовать nuget.org в качестве репозитария, нужно зарегистрировать учетную запись в NuGet Gallery. Стать автором легко: просто перейдите в раздел Contribute в NuGet Gallery по ссылке nuget.org/contribute/index и выберите Get Started. Затем щелкните Register now, чтобы получить регистрационную форму и заполните необходимые поля для создания новой учетной записи. Nuget.org отправит вам сообщение по электронной почте с URL, где вы сможете подтвердить адрес электронной почты и учетную запись.

После подтверждения учетной записи вы можете войти на сайт и получить свой ключ доступа — уникальный маркер, который идентифицирует вас в репозитарии nuget.org и позволяет автоматизировать различные задачи управления пакетами, например передачу обновления для вашего пакета.

В этой статье я продемонстрирую как утилиту командной строки NuGet.exe, так и NuGet Package Explorer. Обратите внимание на одну вещь: скачав версию командной строки, вы, вероятно, захотите обновить переменную окружения Path в своей системе, чтобы включить в нее путь к этой версии. Это позволит легко использовать NuGet в любом месте вашей системы.

Анатомия пакета

NuGet-пакет — это файл контейнера Open Packaging Convention (OPC) с расширением .nupkg. Формат пакета основан на соглашениях, причем файл манифеста в корне называют файлом nuspec. Пример изначальной структуры каталогов может выглядеть так:

Корневая папка
|   package.manifest
+---lib
+---content
+---tools

Как видите, в корне три папки:

  • lib — содержит все сборки, на которые осуществляются ссылки;
  • content — содержит файлы и каталоги, копируемые в корень вашего целевого проекта;
  • tools — место хранения собственных скриптов Windows PowerShell, которые могут выполняться при установке вашего пакета или при каждой загрузке целевого проекта в Visual Studio.

Самый сложный каталог — lib. Он содержит подкаталоги, которые соответствуют зависимостям инфраструктуры:

Корень
| package.manifest
\---lib
    | MyFirstAssembly.dll
    \---net11
        | MySecondAssembly.dll
    \---net20
         | MySecondAssembly.dll
           \---sl
         | MySecondAssembly.dll
    \---netmf
         | MySecondAssembly.dll
+---content
+---tools

Если ваша сборка работает во всех версиях .NET Framework (что бывает крайне редко!), вам нужно включить ее только в корень вашего каталога lib, как в случае моей MyFirstAssembly.dll.

Учитывая, насколько редко встречается такая ситуация, группа NuGet настоятельно советует избегать подобной практики. Лучше сделать вашу сборку зависимой от конкретной версии инфраструктуры. С этой целью добавьте папку для соответствующей версии инфраструктуры и включите в нее правильную версию сборки. Как видно в примере со структурой каталогов, MySecondAssembly.dll имеет разные версии для .NET Framework 1.1, 2.0, Silverlight и .NET MicroFramework. Это гарантирует, что NuGet должным образом установит ваш пакет для целевых инфраструктур.

Когда клиент устанавливает ваш пакет, NuGet установит правильные версии сборок на основании целевой инфраструктуры, применяемой в проекте. Если в предыдущем примере предположить, что ваш клиент пытается установить пакет в проект, ориентированный на .NET Framework 4, то NuGet из-за отсутствия нужного подкаталога в lib выберет ближайшую версию инфраструктуры и будет использовать ее. В данном примере он задействует сборки, найденные в подкаталоге net20.

Папка content является клоном корневой папки целевого проекта. Все, что обнаруживается в этой папке, будет скопировано «как есть» в целевой проект. Скажем, если бы вы хотели скопировать некоторые изображения в папку /images целевого проекта, вам пришлось бы включить эти изображения в папку /content/images.

Папка tools включает любые скрипты Windows PowerShell, запускаемые NuGet при установке пакета или открытии проекта либо используемые клиентом позже. Как только эта папка копируется в целевой проект, она добавляется к переменной окружения `$env:Path (PATH) в Visual Studio Package Manager Console.

В NuGet встроен механизм для автоматизации заполнения пакета — узел 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>

Если ваш процесс создания пакета отличается, вы можете игнорировать узел 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>

После создания этого файла вы заменяете образцовые данные своими. Для большинства таких значений это делается лишь раз, хотя некоторые будет изменяться чаще. Так, идентификатор вашего пакета не должен меняться после публикации, но номер версии будет изменяться при каждом новом выпуске.

Команду spec также можно выполнить применительно к файлу проекта Visual Studio (например, .csproj или .vbproj). В этом случае значения по умолчанию будут заполнены за вас на основе метаданных в файле проекта. Вот некоторые простые элементы файла .nuspec:

  • id — уникальный идентификатор пакета;
  • title — описательное название пакета;
  • description — более подробное описание пакета;
  • summary — краткое описание пакета;
  • licenseURl — ссылка на лицензию;
  • copyrights — сведения об авторских правах на данный пакет.

В настоящее время имеется 28 элементов верхнего уровня. Хотя эти элементы файла .nuspec понятны и без объяснений, описание всех деталей можно найти по ссылке bit.ly/lgQ4J4. Теперь рассмотрим некоторые из более сложных элементов .nuspec.

Зависимости и ссылки

Все мы знаем, как бывает трудно управлять зависимостями, особенно когда их цепочка становится длинной и переплетенной. Допустим, вы создали PackageA. Этот пакет использует PackageB, который тоже можно найти в NuGet. Вместо того чтобы включать PackageB в свой пакет, вы просто создаете «зависимость» от него. Когда кто-то начинает устанавливать ваш пакет, NuGet первым делом проверяет ваш файл nuspec на зависимости. Затем он находит каждый пакет зависимости и изучает его зависимости — и так до тех пор, пока он не построит граф всех пакетов, который нужно скачать для удовлетворения всех зависимостей. После этого он скачивает весь этот граф пакетов и устанавливает их. Эта функциональность NuGet радикально упрощает создание и установку пакетов.

Давайте посмотрим на некоторые зависимости пакета, определенные в узле dependency ниже:

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

Вы можете перечислять столько зависимостей, сколько вам нужно. В каждом случае атрибут id указывает пакет, от которого у вас есть зависимость, а атрибут version представляет диапазон требуемых версий. В моем примере показана зависимость от проекта SampleDependency с версией 1.0 или выше.

Спецификация диапазона версий в 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>

Преобразования

Во многих проектах для корректной работы нужно нечто большее простой ссылки на некую сборку. В них может понадобиться изменение конфигурационного файла или даже части исходного кода — все эти ситуации полностью поддерживаются NuGet. Здесь я основное внимание уделю преобразованиям конфигурационного файла. Подробнее о преобразованиях см. по ссылке bit.ly/jqzry2.

При установке вашего пакета NuGet будет выполнять преобразование, чтобы добавить новые значения в файл .config. С этой целью вам нужно добавить файл преобразования в папку content своего пакета. Это XML-файл с расширением .transformation, а имя файла соответствует имени файла, к которому нужно применить преобразование. Например, чтобы применить преобразование к файлу web.config, вы должны были бы включить файл web.config.transformation.

Ваш файл преобразования должен включать только те разделы конфигурационного файла, которые вы хотите добавить к целевому файлу. Скажем, вам требуется добавить новый модуль в раздел system.webServer. Просто добавьте этот раздел целиком в файл преобразования, например:

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

NuGet не будет заменять существующие разделы добавляемыми разделами — он их объединяет. Поэтому, если в целевом файле уже есть раздел modules со списком модулей, результат объединения двух файлов после установки будет выглядеть так:

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

Как видите, ваш модуль добавлен в конец существующего списка модулей. Если пользователю понадобится удалить ваш пакет, то удаляться будут лишь внесенные вами изменения (при условии, что вы не изменяли сами разделы).

Управление версиями

Управление версиями (versioning) лежит в основе всего, что мы создаем. Версия NuGet-пакета относится к самому пакету, а не к сборкам, содержащимся внутри него (хотя синхронизация их версий весьма желательна). Вы определяете номер версии пакета в файле .nuspec в формате N.N.N.N:

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

В файле .nuspec есть несколько свойств, где можно использовать маркер замены вместо статической строки. Элемент version как раз относится к таким свойствам. Поэтому вместо определения статической строки вроде 1.2.3.4 можно вставить маркер [$version$], который впоследствии будет заменен с помощью NuGet.exe. При наличии этого маркера версия, указанная в AssemblyVersionAttribute сборки, будет перенесена в файл .nuspec:

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

Это отличный вариант, если вам нужно сохранять синхронизацию пакета и версии, хотя есть масса причин на то, чтобы не пользоваться таким вариантом.

Упаковка пакета

Как упоминалось, NuGet-пакет — это файл формата OPC с расширением .nupkg. Чтобы создать пакет в командной строке, вы просто вызываете NuGet.exe с командой pack, передавая ей свой файл .nuspec:

> NuGet.exe Pack YourPackage.nuspec

Как и в случае команды spec, вы можете выполнять pack применительно и к файлу проекта. NuGet создаст полный NuGet-пакет (файл .nupkg), основываясь исключительно на метаданных в вашем файле .csproj или .vbproj. Если вы уже создали файл .nuspec, команда pack задействует его:

> 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 сгенерирует два пакета .nupkg: MyProject.nupkg и MyProject.Symbols.nupkg. Впоследствии файл .symbols.nupkg можно переслать на SymbolSource.org, используя команду push утилиты NuGet.exe. ПОдробнее о создании пакетов символов с помощью NuGet см. по ссылке bit.ly/jqzry2.

Публикация на NuGet.org

Создав пакет, пора заняться его распространением. Для публикации пакета на сервера предназначена NuGet-команда push, и она используется как и большинство современных систем контроля версий исходного кода. В отличие от ранее описанных команд push принимает несколько аргументов:

> NuGet.exe push <package path> [API key] [options]
  • Package path — путь к вашему пакету, например:
    • c:\MyPackge\MyPackage.1.0.nupkg
  • API key — ваш уникальный маркер доступа, например:
    • ABFC2E12-40B3-41A1-A7CC-8FC9AB3A71E0
    • дополнительно этот маркер может быть задан командой setApiKey в NuGet.exe.
  • –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

Кроме того, вы могли бы использовать NuGet Package Explorer, как показано на рис. 2. {Рисунок}

Публикация с помощью NuGet Package Explorer
Рис. 2. Публикация с помощью NuGet Package Explorer

Если вы создали пакет символов, NuGet автоматически найдет его и перешлет в оба репозитария: nuget.org и symbolsource.org. Если целевой компьютер настроен на использование symbolsource.org в качестве источника символов, разработчик сможет при необходимости пошагово выполнять исходный код вашего пакета в Visual Studio.

Вот вы и опубликовали свой пакет! Если это вторая версия вашего пакета, она станет версией по умолчанию. Теперь, когда кто-либо будет искать обновления вашего пакета, он будет перечисляться как пакет с обновлением.

Заключение

Наверняка ваша группа разработки использует некий процесс сборки и развертывания. Уверен, теперь вы задумаетесь о способах интеграции 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) — старший идеолог веб-разработок в Microsoft, работает за пределами Чикаго. Ведет блог csell.net, готовит подкасты на DeveloperSmackdown.com и пишет заметки в twitter.com/csell5.

*Выражаю благодарность за рецензирование статьи экспертам:*Дэвиду Эббо (DavidEbbo), Филу Хааку (PhilHaack), Марку Николсу (MarkNichols) и Брэндону Сэтрому (BrandonSatrom).