{"id":18449,"date":"2013-08-27T17:17:00","date_gmt":"2013-08-27T17:17:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/dotnet\/2013\/08\/27\/nuget-package-restore-with-team-foundation-build\/"},"modified":"2021-09-30T17:23:43","modified_gmt":"2021-10-01T00:23:43","slug":"nuget-package-restore-with-team-foundation-build","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/nuget-package-restore-with-team-foundation-build\/","title":{"rendered":"NuGet Package Restore with Team Foundation Build"},"content":{"rendered":"<p><span style=\"background-color: #ffff99;\"><em><strong>Note: <\/strong>This post is now also part of the official <a href=\"http:\/\/docs.nuget.org\/docs\/reference\/package-restore-with-team-build\"><span style=\"background-color: #ffff99;\">NuGet documentation<\/span><\/a>.<\/em><\/span><\/p>\n<p>In this post, I\u2019ll provide a walkthrough on how to use package restore with <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms181710(v=VS.90).aspx\">Team Foundation Build<\/a> with both, <a href=\"http:\/\/en.wikipedia.org\/wiki\/Git_(software)\">git<\/a> as well <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms181237(v=vs.120).aspx\">TF version control<\/a>.<\/p>\n<p>In our previous blog post on <a href=\"http:\/\/blogs.msdn.com\/b\/dotnet\/archive\/2013\/08\/22\/improved-package-restore.aspx\">how we improved package restore<\/a>, I showed that <a href=\"http:\/\/docs.nuget.org\/docs\/release-notes\/nuget-2.7\">NuGet 2.7<\/a> and <strong>Microsoft.Bcl.Build<\/strong> made restoring packages a lot easier. However, I didn\u2019t explain in great detail on how you can restore packages during the build. In this post, I\u2019ll close that gap by providing a more detailed walkthrough.<\/p>\n<p>Although this walkthrough is specific for the scenario of using <a href=\"http:\/\/tfs.visualstudio.com\/\">Team Foundation Service<\/a>, the concepts also apply to other version control- and build systems.<\/p>\n<h2>The General Approach<\/h2>\n<p>An advantage of using a package manager like NuGet is that you can use it to avoid checking in binaries to your version control system.<\/p>\n<p>This is especially interesting if you are using a <a href=\"http:\/\/en.wikipedia.org\/wiki\/Distributed_revision_control\">distributed version control system<\/a> like git because developers need to clone the entire repository, including the full history, before they can start working locally. Checking in binaries can cause significant repository bloat as binary files are typically stored without delta compression.<\/p>\n<p>NuGet has supported <a href=\"http:\/\/docs.nuget.org\/docs\/reference\/package-restore\">restoring packages<\/a> as part of the build for a long time now. The previous implementation had a chicken-and-egg problem for packages that want to extend the build process because NuGet restored packages while building the project. However, MSBuild doesn\u2019t allow extending the build during the build; one could argue that this an issue in MSBuild but I would argue that this an inherent problem. Depending on which aspect you need to extend it might be too late to register by the time your package is restored.<\/p>\n<p>The cure to this problem is making sure that packages are restored as the first step in the build process. NuGet 2.7 makes this super easy via a <a href=\"http:\/\/docs.nuget.org\/docs\/release-notes\/nuget-2.7#Simplified_Package_Restore_from_the_Command-Line\">simplified command line<\/a>. In order to restore packages for an entire solution all you need is to execute a command line like this:<\/p>\n<pre style=\"padding-left: 30px;\"><code>nuget.exe restore pathtosolution.sln<\/code><\/pre>\n<p>When your build process restores packages before building the code, you don\u2019t need to check-in <strong>.targets<\/strong> files.<\/p>\n<p style=\"padding-left: 30px;\"><em><strong>Note:<\/strong> Packages must be authored to allow loading in Visual Studio (<a href=\"http:\/\/www.nuget.org\/profiles\/dotnetframework\/\">our packages<\/a> now all behave like this). Otherwise, you may still want to check in <strong>.targets<\/strong> files so that other developers can simply open the solution without having to restore packages first.<\/em><\/p>\n<p>The following demo project shows how to set up the build in such a way that the packages folders and .targets files don\u2019t need to be checked-in. Finally, I\u2019ll show how you can setup an automated build on the <a href=\"http:\/\/tfs.visualstudio.com\/\">Team Foundation Service<\/a> for this sample project.<\/p>\n<h2>Repository Structure<\/h2>\n<p>Our demo project is a simple command line tool that uses the command line argument to query Bing. It targets the .NET Framework 4 and uses many of <a href=\"http:\/\/www.nuget.org\/profiles\/dotnetframework\/\">our packages<\/a> (<a href=\"http:\/\/www.nuget.org\/packages\/Microsoft.Net.Http\">Microsoft.Net.Http<\/a>, <a href=\"http:\/\/www.nuget.org\/packages\/Microsoft.Bcl\">Microsoft.Bcl<\/a>, <a href=\"http:\/\/www.nuget.org\/packages\/Microsoft.Bcl.Async\">Microsoft.Bcl.Async<\/a>, and <a href=\"http:\/\/www.nuget.org\/packages\/Microsoft.Bcl.Build\">Microsoft.Bcl.Build<\/a>).<\/p>\n<p>The structure of the repository looks as follows:<\/p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: courier new,courier;\">&lt;Project&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0 .gitignore<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0 .tfignore<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0 build.proj<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2502<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u251c\u2500\u2500\u2500src<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0 \u2502\u00a0\u00a0 BingSearcher.sln<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0 \u2502<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500\u2500BingSearcher<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0 App.config<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0 BingSearcher.csproj<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0 packages.config<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0 Program.cs<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u2502<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u2514\u2500\u2500\u2500Properties<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2502\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 AssemblyInfo.cs<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2502<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 \u2514\u2500\u2500\u2500tools<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 \u2514\u2500\u2500\u2500NuGet<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 NuGet.exe<\/span><\/p>\n<p>You can see that we haven\u2019t checked-in the <code>packages<\/code> folder nor any <strong>.targets<\/strong> files.<\/p>\n<p>We have, however, checked-in the <code>nuget.exe<\/code> as it\u2019s needed during the build. Following widely used conventions we\u2019ve checked it in under a shared <code>tools<\/code> folder.<\/p>\n<p>The source code is under the <code>src<\/code> folder. Although our demo only uses a single solution, you can easily imagine that this folder contains more than one solution.<\/p>\n<p>In order to communicate to the version control that we don\u2019t intent to check-in the <code>packages<\/code> folders, we\u2019ve also added ignore files for both git (<code>.gitignore<\/code>) as well as TF version control (<code>.tfignore<\/code>). These files describes patterns of files you don\u2019t want to check-in.<\/p>\n<p>The <code>.gitignore<\/code> file looks as follows:<\/p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: courier new,courier;\">syntax: glob<\/span>\n<span style=\"font-family: courier new,courier;\">*.user<\/span>\n<span style=\"font-family: courier new,courier;\">*.suo<\/span>\n<span style=\"font-family: courier new,courier;\">bin<\/span>\n<span style=\"font-family: courier new,courier;\">obj<\/span>\n<span style=\"font-family: courier new,courier;\">packages<\/span><\/p>\n<p>The <code>.gitignore<\/code> file is <a href=\"https:\/\/www.kernel.org\/pub\/software\/scm\/git\/docs\/gitignore.html\">quite powerful<\/a>. For example, if you want to generally not check-in the contents of the <code>packages<\/code> folder but want to go with our previous guidance of checking in the <strong>.targets<\/strong> files you could have the following rule instead:<\/p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: courier new,courier;\">packages<\/span>\n<span style=\"font-family: courier new,courier;\">!packages\/**\/*.targets<\/span><\/p>\n<p>This will exclude all <code>packages<\/code> folders but will re-include all contained <strong>.targets<\/strong> files. By the way, you can find a template for <code>.gitignore<\/code> files that is specifically tailored for the needs of Visual Studio developers <a href=\"https:\/\/github.com\/github\/gitignore\/blob\/master\/VisualStudio.gitignore\">here<\/a>.<\/p>\n<p>TF version control supports a very similar mechanism via the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms245454.aspx\">.tfignore file<\/a>. The syntax is virtually the same:<\/p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: courier new,courier;\">*.user<\/span>\n<span style=\"font-family: courier new,courier;\">*.suo<\/span>\n<span style=\"font-family: courier new,courier;\">bin<\/span>\n<span style=\"font-family: courier new,courier;\">obj<\/span>\n<span style=\"font-family: courier new,courier;\">packages<\/span><\/p>\n<h2>build.proj<\/h2>\n<p>For our demo, we keep the build process fairly simple. We\u2019ll create an MSBuild project that builds all solutions while making sure that packages are restored before building the solutions.<\/p>\n<p>This project will have the three conventional targets <code>Clean<\/code>, <code>Build<\/code> and <code>Rebuild<\/code> as well as a new target <code>RestorePackages<\/code>.<\/p>\n<ul>\n<li>The <code>Build<\/code> and <code>Rebuild<\/code> targets both depend on <code>RestorePackages<\/code>. This makes sure that you can both run <code>Build<\/code> and <code>Rebuild<\/code> and rely on packages being restored.<\/li>\n<li><code>Clean<\/code>, <code>Build<\/code> and <code>Rebuild<\/code> invoke the corresponding MSBuild target on all solution files.<\/li>\n<li>The <code>RestorePackages<\/code> target invokes <code>nuget.exe<\/code> for each solution file. This is accomplished by using <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms171473.aspx\">MSBuild\u2019s batching functionality<\/a>.<\/li>\n<\/ul>\n<p>The result looks as follows:<\/p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: courier new,courier;\">&lt;?xml version=&#8221;1.0&#8243; encoding=&#8221;utf-8&#8243;?&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">&lt;Project ToolsVersion=&#8221;4.0&#8243;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 DefaultTargets=&#8221;Build&#8221; <\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 xmlns=&#8221;<a href=\"http:\/\/schemas.microsoft.com\/developer\/msbuild\/2003\">http:\/\/schemas.microsoft.com\/developer\/msbuild\/2003<\/a>&#8220;&gt;<\/span><\/p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: courier new,courier;\">\u00a0 &lt;PropertyGroup&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 &lt;OutDir&gt;$(MSBuildThisFileDirectory)bin&lt;\/OutDir&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 &lt;Configuration&gt;Release&lt;\/Configuration&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 &lt;ProjectProperties&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 OutDir=$(OutDir);<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Configuration=$(Configuration);<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 &lt;\/ProjectProperties&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0 &lt;\/PropertyGroup&gt;<\/span><\/p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: courier new,courier;\">\u00a0 &lt;ItemGroup&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 &lt;Solution Include=&#8221;$(MSBuildThisFileDirectory)src*.sln&#8221; \/&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0 &lt;\/ItemGroup&gt;<\/span><\/p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: courier new,courier;\">\u00a0 &lt;Target Name=&#8221;RestorePackages&#8221;&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 &lt;Exec Command=&#8221;&amp;quot;$(MSBuildThisFileDirectory)toolsNuGetNuGet.exe&amp;quot; restore &amp;quot;%(Solution.Identity)&amp;quot;&#8221; \/&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0 &lt;\/Target&gt;<\/span><\/p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: courier new,courier;\">\u00a0 &lt;Target Name=&#8221;Clean&#8221;&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 &lt;MSBuild Targets=&#8221;Clean&#8221;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Projects=&#8221;@(Solution)&#8221;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Properties=&#8221;$(ProjectProperties)&#8221; \/&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0 &lt;\/Target&gt;<\/span><\/p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: courier new,courier;\">\u00a0 &lt;Target Name=&#8221;Build&#8221; DependsOnTargets=&#8221;RestorePackages&#8221;&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 &lt;MSBuild Targets=&#8221;Build&#8221;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Projects=&#8221;@(Solution)&#8221;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Properties=&#8221;$(ProjectProperties)&#8221; \/&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0 &lt;\/Target&gt;<\/span><\/p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: courier new,courier;\">\u00a0 &lt;Target Name=&#8221;Rebuild&#8221; DependsOnTargets=&#8221;RestorePackages&#8221;&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0 &lt;MSBuild Targets=&#8221;Rebuild&#8221;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Projects=&#8221;@(Solution)&#8221;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 Properties=&#8221;$(ProjectProperties)&#8221; \/&gt;<\/span>\n<span style=\"font-family: courier new,courier;\">\u00a0 &lt;\/Target&gt;<\/span><\/p>\n<p style=\"padding-left: 30px;\"><span style=\"font-family: courier new,courier;\">&lt;\/Project&gt;<\/span><\/p>\n<h2>Configuring Team Build<\/h2>\n<p>Team Build offers various process templates. For this demonstration, I\u2019m using the <a href=\"http:\/\/tfs.visualstudio.com\/\">Team Foundation Service<\/a>. On premise installations of TFS will be very similar though.<\/p>\n<p>Git and TF Version Control have different Team Build templates, so the following steps will vary depending on which version control system you are using. In both cases, all you need is selecting the <strong>build.proj<\/strong> as the project you want to build.<\/p>\n<p>First, let\u2019s look at the process template for git. In the git based template the build is selected via the property <strong>1. Solution to build<\/strong>:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2013\/08\/2627.PackageRestoreTeamBuildGit.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2013\/08\/2627.PackageRestoreTeamBuildGit.png\" alt=\"\" border=\"0\" \/><\/a><\/p>\n<p>Please note that this property is a location in your repository. Since our <strong>build.proj<\/strong> is in the root, we simply used <strong>build.proj<\/strong>. If you place the build file under a folder called <strong>tools<\/strong>, the value would be <strong>toolsbuild.proj<\/strong>.<\/p>\n<p>In the TF version control template the project is selected via the property <strong>1. Projects<\/strong>:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2013\/08\/1004.PackageRestoreTeamBuildTFVC.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2013\/08\/1004.PackageRestoreTeamBuildTFVC.png\" alt=\"\" border=\"0\" \/><\/a><\/p>\n<p>In contrast to the git based template the TF version control supports pickers (the button on the right hand side with the three dots). So in order to avoid any typing errors I suggest you use them to select the project.<\/p>\n<h2>Summary<\/h2>\n<p>Using NuGet <a href=\"http:\/\/docs.nuget.org\/docs\/reference\/package-restore\">package restore<\/a> is a great way to avoid checked-in libraries. In this walkthrough I\u2019ve demonstrated how you can set up a repository structure and a build process for a project that avoids this.<\/p>\n<p>Please let us know what you think!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Note: This post is now also part of the official NuGet documentation. In this post, I\u2019ll provide a walkthrough on how to use package restore with Team Foundation Build with both, git as well TF version control. In our previous blog post on how we improved package restore, I showed that NuGet 2.7 and Microsoft.Bcl.Build [&hellip;]<\/p>\n","protected":false},"author":335,"featured_media":58792,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685],"tags":[43,104,110],"class_list":["post-18449","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","tag-bcl","tag-nuget","tag-portable-class-libraries"],"acf":[],"blog_post_summary":"<p>Note: This post is now also part of the official NuGet documentation. In this post, I\u2019ll provide a walkthrough on how to use package restore with Team Foundation Build with both, git as well TF version control. In our previous blog post on how we improved package restore, I showed that NuGet 2.7 and Microsoft.Bcl.Build [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/18449","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/335"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=18449"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/18449\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/58792"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=18449"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=18449"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=18449"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}