Skip to content

Commit

Permalink
Add automatic cleanups to improve the inner devloop
Browse files Browse the repository at this point in the history
When working with packages built locally, it's quite common to dogfood those packages from sample projects locally. This can use a fixed package version for the locally produced packages, or auto-incremented versions. Also, the referencing projects might choose to use wildcards when testing local packages too.

The caching mechanisms built into NuGet make this process a bit more cumbersome than necessary: if you build a fixed version package, you will never get a newly built version restored in a project elsewhere in the machine because NuGet will believe the one in the cache is already the latest. The HTTP-level cache implemented on top of the package cache also works against you in that case even if you clean that folder. And wildcards don't make things much better unless you clean those caches too.

In addition, if you increment package versions when building locally too, the package output path will continuously be filled up with older versions unnecessarily.

This commit adds support for automatically fixing all those issues while still causing minimal disruption or performance problems for other packages and projets in your machine, as follows:

* The entire cleanup only is in place for packable projects, and in local (non-CI) builds
* It can be turned off entirely by setting `EnablePackCleanup=false`.
* It cleans the specific package folder in the cache for the current PackageId: nuget creates a subfolder in the package cache dir for each package id, and places all versions inside. By removing just that folder, you effectively clean the cache for that package and no others.
* It cleans the HTTP cache too: this cannot be done selectively for a specific package id, and therefore can be turned off by setting `CleanHttpNuGetCacheOnPack=false` if it causes performance issues. In my experience, it doesn't since the HTTP cache is just an optimization for offline scenarios (I think?).

I have used this approach for years on multiple projects with multiple packaging approaches and at this point I think it deserves being built-in in nugetizer.
  • Loading branch information
kzu committed Oct 20, 2020
1 parent 568b376 commit 58d54ce
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 1 deletion.
54 changes: 54 additions & 0 deletions src/NuGetizer.Tasks/NuGetizer.Cleanup.targets
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<!--
***********************************************************************************************
NuGetizer.Cleanup.targets
Cleans up previously built packages from the package output folder, as well
as the local caches so that restoring packages (even using wildcards) will
always pick the freshly built ones.
Copyright (c) .NET Foundation. All rights reserved.
***********************************************************************************************
-->
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

<PropertyGroup Label="Hidden">
<HttpNuGetCache>$(LocalAppData)\NuGet\v3-cache</HttpNuGetCache>
<!-- We clean the HTTP cache by default. This does *not* clear the cached installed packages -->
<CleanHttpNuGetCacheOnPack Condition="'$(CleanHttpNuGetCacheOnPack)' == ''">true</CleanHttpNuGetCacheOnPack>
<!-- The actual NuGet cache is only cleared for the *current* PackageId, so no need to turn off its clearing on build/pack -->
<NuGetCache>$(UserProfile)\.nuget\packages</NuGetCache>
</PropertyGroup>

<Target Name="CleanPackageOutput" BeforeTargets="Build">
<ItemGroup>
<_ExistingPackage Include="$(PackageOutputPath)\$(PackageId)*.nupkg" />
<_PackageToDelete Include="@(_ExistingPackage)"
Condition="$([System.Text.RegularExpressions.Regex]::IsMatch('%(Filename)', '$(PackageId)\.\d\.\d\.\d.*'))" />
</ItemGroup>
<Delete Files="@(_PackageToDelete)" ContinueOnError="true">
<Output TaskParameter="DeletedFiles" ItemName="_CleanedPackages" />
</Delete>
<Message Text="Cleaned existing packages: @(_CleanedPackages -> '%(Filename)%(Extension)')"
Condition="'@(_CleanedPackages)' != ''" />
</Target>

<!-- Clears nuget cache for the current project package id -->
<Target Name="CleanCachedPackageId" AfterTargets="Build;Pack">
<PropertyGroup>
<PackageFolder>$(NuGetCache)\$(PackageId.ToLowerInvariant())</PackageFolder>
</PropertyGroup>

<Message Text="Cleaning $(PackageFolder)" Condition="Exists($(PackageFolder))" />
<Exec Command='rd "$(PackageFolder)" /q /s' Condition="Exists($(PackageFolder)) and '$(OS)' == 'Windows_NT'" />
<Exec Command='rm -rf "$(PackageFolder)"' Condition="Exists($(PackageFolder)) and '$(OS)' != 'Windows_NT'" />
</Target>

<Target Name="CleanHttpNuGetCache"
Condition="'$(CleanHttpNuGetCacheOnPack)' == 'true' and Exists('$(HttpNuGetCache)')"
AfterTargets="Build;Pack">
<Message Text="Cleaning $(HttpNuGetCache)" />
<Exec Command='rd "$(HttpNuGetCache)" /q /s' Condition="'$(OS)' == 'Windows_NT'" />
<Exec Command='rm -rf "$(HttpNuGetCache)"' Condition="'$(OS)' != 'Windows_NT'" />
</Target>

</Project>
5 changes: 5 additions & 0 deletions src/NuGetizer.Tasks/NuGetizer.Shared.targets
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ Copyright (c) .NET Foundation. All rights reserved.
<!-- Whether to infer package contents -->
<EnablePackInference Condition="'$(EnablePackInference)' == ''">true</EnablePackInference>

<!-- Whether to cleanup package output folder and nuget caches on pack -->
<EnablePackCleanup Condition="'$(EnablePackCleanup)' == ''">true</EnablePackCleanup>

<!-- Whether include referenced projects' contents in the package. -->
<PackProjectReference Condition="'$(PackProjectReference)' == ''">true</PackProjectReference>

Expand Down Expand Up @@ -271,5 +274,7 @@ Copyright (c) .NET Foundation. All rights reserved.
<Message Importance="high" Text="Created package at %(_PackageTargetPath.FullPath)." Condition="'$(EmitPackage)' == 'true'" />
</Target>

<!-- Cleanups improve the local development loop, by always ensure the fresly built packages can be restored from output -->
<Import Project="NuGetizer.Cleanup.targets" Condition="'$(CI)' != 'true' and '$(EnablePackCleanup)' != 'false' and '$(IsPackable)' == 'true' and '$(PackageId)' != ''" />
<Import Project="NuGetizer.PackageMetadata.targets" Condition="'$(UsingMicrosoftNETSdk)' != 'true'" />
</Project>
14 changes: 13 additions & 1 deletion src/NuGetizer.Tasks/NuGetizer.props
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,19 @@ Copyright (c) .NET Foundation. All rights reserved.
<NuGetBuildTasksPackTargets>$(MSBuildThisFileDirectory)NuGetizer.PackageMetadata.targets</NuGetBuildTasksPackTargets>
<ImportNuGetBuildTasksPackTargetsFromSdk>true</ImportNuGetBuildTasksPackTargetsFromSdk>
</PropertyGroup>


<PropertyGroup Label="Hidden" Condition="'$(CI)' == ''">
<CI>false</CI>
<!-- GH, CircleCI, GitLab and BitBucket already use CI -->
<CI Condition="'$(TF_BUILD)' == 'true' or
'$(TEAMCITY_VERSION)' != '' or
'$(APPVEYOR)' != '' or
'$(BuildRunner)' == 'MyGet' or
'$(JENKINS_URL)' != '' or
'$(TRAVIS)' == 'true' or
'$(BUDDY)' == 'true'">true</CI>
</PropertyGroup>

<ItemGroup>
<AvailableItemName Include="PackageFile" />
</ItemGroup>
Expand Down

0 comments on commit 58d54ce

Please sign in to comment.