Open Crossbow78 opened 4 years ago
Are you passing any other arguments to build and publish, esp. regarding runtime identifier?
(e.g. not passing -r win-x64
to dotnet builld
but to dotnet publish --no-build
)
Yes, only the publish command has more arguments, and looks like this:
dotnet publish MyApp.Client.csproj --no-build -r win-x64
(in its basic form, I omitted any specific parameters like PublishSingleFile)
And this is included in the project file:
<RuntimeIdentifiers>win-x64</RuntimeIdentifiers>
So does it change if you add the -r win-x64
to the dotnet build
command as well?
This is the part that produces the contents in bin\Debug\netcoreapp3.1\win-x64
that the subsequent publish would already expect to be there.
Here's two more observations:
(1)
When I replace that line in the project file with the singular form (and I suppose that's the equivalent of the command line option):
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
the publish step succeeds! I also noticed that the build output is then indeed placed under a win-x64
subfolder, which is what the publish step was looking for. Still leaves me wondering how to publish for multiple different runtimes.
(2)
I was signing bin\Debug\netcoreapp3.1\win-x64\MyApp.Client.dll
which is overwritten during the publish (even when using --no-build
).
But when I sign the one under obj
instead, that one seems to be 'reused' (and the --no-build
option actually plays a role there) and will successfully end up into the published output.
I'm not entirely sure whether all of this is obvious (or even expected) behavior...
The plural RuntimeIdentifiers
in the cspros is mostly a no-op - it tells nuget to prepare assets for the RIDs (e.g. before we had implicit restore in 2.0+). It is not needed any more. And it does not affect any build or publish behaviour on its own.
The singular RuntimeIdentifier
is the same as passing -r
to the CLI.
The assembly published will be the one in obj/[Configuration]/[TargetFramework]/[RuntimeIdentifier] for the project being published and in bin/... of the respective library folders.
Aha, that clears things up, thanks! I suppose I should sign the bin output for class library assemblies, and obj output for the application assemblies, even though it feels like relying on internal 'dotnet publish' behavior that could change any moment.
One last question, why does the build step require a runtime identifier at all? Does it compile differently when targeting different runtimes?
It mostly shouldn't matter, except for when it does.
Say you reference the EF Core SQLite provider and call dotnet bulid
, you'll get a runtimes/
folder with all kinds of different native code for different platforms. If you pass -r win-x64
, you'll only get the 64-bit windows dll and not the binaries for macOS or linux as well.
NuGet packags can provide different implementations and even dependency grpahs for different runtimes ("bait-and-switch" packages).
Some project authors also choose to customize build settings for different RIDs by using a few msbuild condition inside the csproj (I recommend against that and relying on runtime checks wherever possible).
You could also trigger signing from within the build process itself.
E.g. create a file named Directory.Build.props
in your project root directory (e.g. next to the .sln file) containing:
<Project>
<PropertyGroup>
<TargetsTriggeredByCompilation>$(SignIntermediateAssembly);SignIntermediateAssembly</TargetsTriggeredByCompilation>
</PropertyGroup>
<Target Name="SignIntermediateAssembly">
<Exec Command="signtool.exe ... %(IntermediateAssembly.FullPath)" />
</Target>
<Target Name="SignPublishedSingleFileBundle" AfterTargets="BundlePublishDirectory">
<Exec Command="signtool.exe ... $(PublishedSingleFilePath)" />
</Target>
</Project>
Interesting, that Directory.Build.Props
definitely feels like a cleaner way to achieve what I want.
In the pipeline I have the certificate's binary content sitting in pipeline variable after retrieving it from the keyvault, so I should probably dump it to disk before I could use signtool
.
~Lastly, I'm currently using dotnet publish
, but this build customization requires MSBuild
right?~
Edit: I found that this target is automatically invoked even during dotnet build
, which makes it fail when run during a local build (from within VS) since the certificate is not available there.
You can add an Condition="'$(SignAssemblies)' == 'True'"
attribute to the Target
elements and then pass -p:SignAssemblies=True
as argument to dotnet build
/publish
if you only want to run that during CI.
Any pipeline variable should also be an environment variable during builds (or you can define extra env vars in the pipeline's YAML file).
And all environment variables are automatically global MSBuild properties, thus you should be able to access pipeline variables in MSBuild using $(MY_VARIABLE)
(replacing any dots with underscore).
Hi,
Do we have now out of the box support to sign dlls without workarounds (directory build.props) etc? at this point: we have "dotnet nuget sign nupkg" however the dlls remain unsigned... the current workflow is very weird to say the least:
It should be possible to do: "dotnet build --sign --cert -timestamp" as we do for the nuget package... Alternatively my prefered approach, "dotnet nuget sign nupkg" should also sign the dlls
By the way, azure nuget packages everything is signed, nuget plus dlls... Is there some magic that can do all of it whiteout calling sn.exe?
PS: there is no publish step involved, its a client library. "dotnet build" should be enough build the packages and generate the signatures...
@sfoslund @dasMulli thanks in advance
Using .NET Core SDK 3.1.
We're trying to publish our client application with strong-named assemblies, as a single-file application.
So we setup an Azure Build Pipeline with the following outline:
dotnet build
to produce output assembliesSet-AuthenticodeSignature
to sign output assemblies with certificate (note: we could also re-sign a partially signed assembly using 'sn.exe', but the idea is the same)dotnet publish --no-build
to produce a self-contained, single-file applicationThe
--no-build
is causing issues:If we omit the
--no-build
flag everything succeeds, but it also rebuilds a fresh dll which is not signed.You might suggest that we should first publish, and then apply the strong-name signing on the published output. But since the publish step produces a single-file application, we must sign our assemblies before they are wrapped into that single-file container... so how is this supposed to work? And why is
--no-build
behaving so weirdly?