dotnet / android

.NET for Android provides open-source bindings of the Android SDK for use with .NET managed languages such as C#
MIT License
1.92k stars 526 forks source link

Running dotnet publish after dotnet build doesn't sign apk using keystore #8476

Open JensSchadron opened 11 months ago

JensSchadron commented 11 months ago

Android application type

.NET Android (net7.0-android, etc.)

Affected platform version

.NET 8 RC2 / .NET 7, MacOS Ventura 13.5.2

Description

I noticed that when using dotnet publish after having issued the dotnet build command, will ignore the provided keystore information in the dotnet publish command, which seemed kinda counterintuitive. Reason why I'm using dotnet build first is to ensure that everything actually compiles, then subsequently run all unit tests, and if all those are successful, we create the actual signed apk.

However... when doing so while having more verbose logs, I get informed that the _sign build target was a no-op. See screenshot below.

image

Steps to Reproduce

  1. Download this zip => SigningIssueReproApp.zip (it's pretty much a new android application, but is easier to follow with the commands used below)
  2. Optional: Create new keystore (see docs https://learn.microsoft.com/en-us/dotnet/maui/android/deployment/publish-cli#create-a-keystore-file), there's one already included in the zip to make things easier
  3. Open terminal at the root of the repository and run dotnet restore
  4. Run dotnet build source/SigningIssueReproApp/SigningIssueReproApp.csproj --configuration Release --no-restore (generated apk gets signed by a debug.keystore)
  5. Run dotnet publish source/SigningIssueReproApp/SigningIssueReproApp.csproj --configuration Release --no-restore --output artifacts/ /property:AndroidKeyStore=True /property:AndroidSigningKeyStore=/Users/jens/Documents/Projects/SigningIssueReproApp/deploy/myapp.keystore /property:AndroidSigningKeyAlias=myapp /property:AndroidSigningKeyPass=password /property:AndroidSigningStorePass=password (make sure to adjust the path to the keystore as the CLI apparently somehow requires an absolute path to that file)
  6. Use keytool to check the certificate that was used to sign the apk in the newly created 'artifacts' directory and observe that our self-provided keystore wasn't used.

Did you find any workaround?

A workaround that was provided to me by a colleague, which isn't exactly clean to me, is to also provide all the keystore information to dotnet build. I'm currently not aware about any other possible workarounds that work.

Relevant log output

1>Target "_Sign" in file "/usr/local/share/dotnet/packs/Microsoft.Android.Sdk.Darwin/33.0.95/tools/Xamarin.Android.Common.targets" from project "/Users/jens/Documents/Projects/SigningIssueReproApp/source/SigningIssueReproApp/SigningIssueReproApp.csproj" (target "Build" depends on it):
       Skipping target "_Sign" because all output files are up-to-date with respect to the input files.
       Input files: 
           /Users/jens/Documents/Projects/SigningIssueReproApp/source/SigningIssueReproApp/obj/SigningIssueReproApp.csproj.nuget.g.targets
           /usr/local/share/dotnet/sdk/8.0.100-rc.2.23502.2/Current/Microsoft.Common.targets/ImportAfter/Microsoft.TestPlatform.ImportAfter.targets
           obj/Release/net7.0-android/build.props
           obj/Release/net7.0-android/android/bin/com.companyname.SigningIssueReproApp.apk
       Output files: bin/Release/net7.0-android/com.companyname.SigningIssueReproApp-Signed.apk
     1>Done building target "_Sign" in project "SigningIssueReproApp.csproj".
dellis1972 commented 11 months ago

@JensSchadron the apk/aab is built as part of the dotnet build step. All publish does is copy the outputs to the publish directory. This is by design. So in order to get the results you want you should set the appropriate signing keys for the dotnet build call.

JensSchadron commented 11 months ago

@dellis1972 Just to make sure I understand correctly, I can remove all the property arguments from the publish step, straight to the build step and get the same result? Or should they be added to both build and publish commands?

dellis1972 commented 11 months ago

They are only required on the build step I think. That said I'm not sure if our property Cache will cause a build if they are not provided in the publish. But based on the fast the _Sign target is skipped when do you provide them it should work that way.

The reason for all this is the publsh command is not called as part of the Debug loop in VS. As a result if we don't produce the apk in the build step there would be nothing to debug. So the publish command really is just a copy apk to output step.

dellis1972 commented 11 months ago

@jonathanpeppers what I've said is right isn't it? my memory is a bit fuzzy on some of the changes we did for .net 6.

JensSchadron commented 11 months ago

I see, thanks for clarifying. I'll experiment a little bit more with it then. Will also close the issue as soon as Jonathan peppers has confirmed it then. 🙂

JensSchadron commented 11 months ago

I think that it might also be worthwhile to mention this behavior in the docs

jonathanpeppers commented 11 months ago

Yes dotnet publish doesn't do much:

https://github.com/xamarin/xamarin-android/blob/main/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.Publish.targets

It basically adds the right files to @(ResolvedFileToPublish) so they are copied to $(PublishDir). You can just use dotnet build if you like on Android, and never use dotnet publish.

Is the underlying problem actually:

Can you verify if you see the same thing with dotnet build? That probably points to this issue, if so.

JensSchadron commented 11 months ago

Not sure if it could be called an incremental build if there's only a change in the keystore settings, but yes it basically boils down to that.

jonathanpeppers commented 11 months ago

Yes, I think it really is an issue then -- incremental build is basically any build where bin and obj had files leftover IMO.