Note: this is not a production-proven workflow, take it as lab notes I wrote while I still remembered how it works.
When developing a NuGet package, you easily end up with situation where you depend on another package published from the same (mono)repository.
Example:
- I have FastExpressionKit targeting
netstandard2.0
- I have FastExpressionKit.BulkInsert targeting
net472
(becausenetstandard2.0
is missingSystem.ComponentModel.DataAnnotations
) - I have FastExpressionKit.Test that runs tests against both of the libraries.
FastExpressionKit.Test needs to use ProjectReference
instead of PackageReference
, because you couldn't run the test suite against unpublished packages. You also want a fast edit/run inner development loop.
I solved this dilemma by doing the following for each csproj:
$ dotnet pack -c Release /p:Version=1.1.0 /p:PubVer=1.1.0
Here, PubVer
is the toggle that switches on PackageReference. Excerpt from FastExpressionKit.BulkInsert.csproj:
<ItemGroup Condition="$(PubVer) == ''">
<ProjectReference Include="..\FastExpressionKit\FastExpressionKit.csproj" />
</ItemGroup>
<ItemGroup Condition="$(PubVer) != ''">
<PackageReference Include="FastExpressionKit" Version="$(PubVer)" />
</ItemGroup>
When running tests, PubVer
is not defined and ProjectReference is activated. That wasn't so bad, especially since you can slap the Condition
on ItemGroup
instead of reference itself.
Local feeds
In order to create a package for FastExpressionKit.BulkInsert, it needs access to FastExpressionKit package. For a version you don't want to publish yet. Hence we publish it to local feed immediately after dotnet pack
:
$ dotnet nuget push -s c:\localfeed .\bin\Release\FastExpressionKit.1.2.0.nupkg
In order to actually use this local repository, you have to create file called NuGet.Config (case sensitive!) with content (adjust relative path accordingly):
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<add key="local" value="..\..\localfeed" />
</packageSources>
</configuration>
When this is in place, PackageReference will find the FastExpressionKit package from that local directory.
Why do it like this???
- NuGet, unlike pip or npm, doesn't have built-in support for this workflow so tricks are needed
- Paket.local doesn't support SDK-style project files
- For convenience, the version of all NuGet packages in the FastExpressionKit "package family" is the same, i.e. one specified by both
PubVer
andVersion
- I didn't really run those commands, I have publish.py script that does these steps. For the sake of historical correctness, here's the snapshot of the script:
from pathlib import Path
import os,shutil
LOCAL_FEED = Path(r".\dev\localnuget").absolute()
projects = ["FastExpressionKit", "FastExpressionKit.BulkInsert"]
version = "1.2.0.0"
def c(s):
print(">",s)
err = os.system(s)
assert not err
def nuke(pth):
if os.path.isdir(pth):
shutil.rmtree(pth)
def nuget_add(pth):
c(f"dotnet nuget push -s {LOCAL_FEED} {pth}")
startdir = Path(".").absolute()
for prjdir in projects:
os.chdir(startdir / prjdir)
nuke("bin")
nuke("obj")
def pack():
c(f"dotnet pack -c Release /p:Version={version} /p:PubVer={version}")
pkgs = list(Path("bin/Release").glob("*.nupkg"))
nuget_add(pkgs[0])
pack()
Got a better workflow that is not a massive MsBuild XML library? Let us know in the comments!
Top comments (0)