Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.NET MAUI Android target publishing/archiving #4377

Closed
jfversluis opened this issue Jan 28, 2022 · 29 comments
Closed

.NET MAUI Android target publishing/archiving #4377

jfversluis opened this issue Jan 28, 2022 · 29 comments
Assignees
Labels
area-publishing Issues with the app packaging/publishing process (ipk/apk/msix/trimming) platform/android 🤖

Comments

@jfversluis
Copy link
Member

jfversluis commented Jan 28, 2022

TL;DR

  • Archiving and publishing a .NET MAUI Android app (both .NET MAUI and .NET MAUI Blazor) to an aab works. See the steps below.
    • I have been able to upload it to the Google Play Store and then download it to test the app. The regular .NET MAUI app works without doing anything special, for .NET MAUI Blazor you need to follow the instructions in this comment. I think for the next preview this should be fixed and working out-of-the-box.
  • This is all through the command-line, UI support in Visual Studio is not there (yet)

Description

I have been trying to see if publishing an Android app through .NET MAUI works at the moment. This description assumes that you have basic knowledge of publishing to the Google Play Store.

1. Prerequisites

  • All the required tools and bits are installed to create and run .NET MAUI Android apps successfully
  • Make sure that your AndroidManifest.xml file contains a valid version number and package identifier (i.e. com.mycompany.app)

2. Create keystore file

This process is no different between Xamarin, Xamarin.Forms, .NET MAUI or even a native app. Run the following command to create a new keystore file. If you already have an existing keystore file, you can skip this step.

Make sure you know where you're running this command from so you know where the keystore is created. Alternatively, provide a full path for the -keystore option.

keytool -genkey -v -keystore key.keystore -alias key -keyalg RSA -keysize 2048 -validity 10000

Note: make sure the validity is a high number (number is in days). The Google Play Store will not accept low numbers and if the keystore is expired, you will not be able to upload updates for your app anymore. Also don't lose your keystore file, you will need that for any updates to your app. Without it, you cannot upload updates to your app.

Make note of the value in -alias this can be anything, but you will need it later. You will also need to provide a password. Needless to say, you will need that one and need to remember it as well. For detailed instructions, see this existing Docs page.

3. Update your csproj file

We need to add the information we got from above into our csproj file. If you have an existing app, you can potentially copy this from your Android csproj file. The information will look like this, and needs to be contained within the <Project></Project> node.

<PropertyGroup Condition="$(TargetFramework.Contains('-android')) and '$(Configuration)' == 'Release'">
    <AndroidKeyStore>True</AndroidKeyStore>
    <AndroidSigningKeyStore>filename.keystore</AndroidSigningKeyStore>
    <AndroidSigningStorePass>filename.keystore password</AndroidSigningStorePass>
    <AndroidSigningKeyAlias>keystore.alias</AndroidSigningKeyAlias>
    <AndroidSigningKeyPass>keystore.alias password</AndroidSigningKeyPass>
</PropertyGroup>

This information will only be added if you are building the Android target and building the release configuration. Tweak this as needed.

I think all the info speaks for itself, add the path to your keystore file along with the alias and corresponding passwords.

4. Build your .NET MAUI Android app

Navigate to the folder that holds the source for your .NET MAUI Android app and execute the following command:

dotnet publish -f:net6.0-android -c:Release

This will create a couple of files under the bin\Release\net6.0-android\publish folder. There should be a file that will have the name of your package identifier and that mentions "-Signed". For example, look for: com.mycompany.app-Signed.aab.

For the Google Play Store you will need the aab file, for other purposes you can use the apk file that is also in there.

Notes

  • Instead of specifying all the options in the csproj file, they can also be added to the dotnet publish command. E.g. <AndroidSigningKeyPass>mypassword</AndroidSigningKeyPass> would become /p:AndroidSigningKeyPass=mypassword.

Related Resources

Also See

@jfversluis jfversluis added platform/android 🤖 area-publishing Issues with the app packaging/publishing process (ipk/apk/msix/trimming) labels Jan 28, 2022
@jfversluis
Copy link
Member Author

jfversluis commented Jan 28, 2022

@mkArtakMSFT tagging you here as well, as you seem to be the right person that was also tagged in the Windows issue for this. The .NET MAUI Blazor app seems to crash right after launching with this one.

I updated my original comment. For making it work with .NET MAUI Blazor app see here: #2246 (comment)

This should be working out-of-the-box from the next preview

@jonathanpeppers
Copy link
Member

I'm curious, why do you need to run zipalign & sign yourself at all?

We have MSBuild properties for configuring signing:

https://docs.microsoft.com/en-us/xamarin/android/deploy-test/building-apps/build-process#signing-properties

These are the same in Xamarin.Android and .NET 6.

It seems like you could just use:

<PropertyGroup Condition="'$(Configuration)' == 'Release'">
    <AndroidKeyStore>True</AndroidKeyStore>
    <AndroidSigningKeyStore>filename.keystore</AndroidSigningKeyStore>
    <AndroidSigningStorePass>keystore.filename password</AndroidSigningStorePass>
    <AndroidSigningKeyAlias>keystore.alias</AndroidSigningKeyAlias>
    <AndroidSigningKeyPass>keystore.alias password</AndroidSigningKeyPass>
</PropertyGroup

Then you just dotnet build -c Release and use the .aab file that pops out?

@jfversluis
Copy link
Member Author

jfversluis commented Jan 31, 2022

Probably because we don't need to, but I simply wasn't aware 😅

I did see a signed version being created, but it could impossibly picking up my keystore file, so I figured I had to go through these things if I wanted to do it manually. I'm assuming this will indeed still work, but will verify and update the initial comment. Thanks!

Need to still update the initial post at the top here, but the suggestion by Jonathan works prefectly.

Updated in initial comment above.

@Ghevi
Copy link

Ghevi commented Feb 18, 2022

 <AndroidSigningStorePass>keystore.filename password</AndroidSigningStorePass>

Sorry i'm not very familiari with these procedures, is "keystore.filename" correct or should be "filename.keystore"?

@jfversluis
Copy link
Member Author

@Ghevi whoops! I will fix that now, however not that this is not a fixed value. This should be the password that corresponds to your keystore file :)

Maybe this video I made about it makes things more clear as well: https://www.youtube.com/watch?v=jfSVb_RR7X0

@xperiandri
Copy link

Setting OutDir on publish produces no output to PublishDir

@xperiandri
Copy link

We want to do a build on Azure DevOps to Binaries directory by specifying OutDir and then publish from it by specifying OutDir and PublishDir. This does not work

@Juriyx
Copy link

Juriyx commented Mar 14, 2022

Here is the binary log of MAUI project for @xperiandri's scenario
MauiApp1.zip

@jfversluis
Copy link
Member Author

Could you please give an example of the full command that you are trying to run?

@Juriyx
Copy link

Juriyx commented Mar 18, 2022

Could you please give an example of the full command that you are trying to run?

dotnet publish --no-restore -f net6.0-android -c Release /p:OutDir="C:\С Temp\B Android" -o "C:\С Temp\A Android" /p:BuildProjectReferences=false /p:AndroidKeyStore=True /p:AndroidSigningKeyStore="ITM Transmission.keystore" /p:AndroidSigningStorePass=PASSWORD /p:AndroidSigningKeyAlias="ITM Transmission" /p:AndroidSigningKeyPass=PASSWORD

Also tried with --no-build parameter instead of --no-restore when the project was built before that, but got the error "The 'NoBuild' property was set to true but the 'Build' target was invoked"

@jfversluis
Copy link
Member Author

I think this is a problem in the dotnet CLI tooling and not so much with us: dotnet/sdk#9012

As suggested in the linked issue, would you be able to try -p:PublishDir=.\publish? Maybe that works for you?

@Juriyx
Copy link

Juriyx commented Mar 18, 2022

As suggested in the linked issue, would you be able to try -p:PublishDir=.\publish?

Do I understand right that this parameter doesn't support explicit paths?

As I remember, I have already tried it and it didn't come up with me, because I wanted a command for the pipeline in Azure DevOps to specify an explicit path to folders so I used /p:OutDir=$(BuildPath) -o $(Build.ArtifactStagingDirectory)

@Juriyx
Copy link

Juriyx commented Mar 19, 2022

As suggested in the linked issue, would you be able to try -p:PublishDir=.\publish? Maybe that works for you?

I checked locally another variant, parameters /p:OutDir="C:\Temp\B Android" /p:PublishDir="C:\Temp\A Android" work for me as expected. But on Azure DevOps copying apk and aab packages from the build directory to the artifacts directory doesn't happen for some reason.
Do you know why it can be this way?

@jfversluis
Copy link
Member Author

If it works locally but not on Azure DevOps then there must be something different in these environments. Does it run the
exact same version of .NET? Is it both running on the same OS? But in either case, it doesn't seem like a problem on our side. Not that I just want to ship you off with "not our problem", but mostly because we're then also not able to do anything about it. Are you using some kind of build task? Maybe open an issue on their repository?

@jonathanpeppers
Copy link
Member

@Juriyx this example works for me with MAUI Preview 14:

dotnet new maui
dotnet publish -c Release -f net6.0-android -o test

Then I have these files in test:

com.companyname.foo-Signed.aab
com.companyname.foo-Signed.apk
com.companyname.foo.aab

I tried a folder with spaces, but that works for me as well.

If it's failing, can you add -bl and share the msbuild.binlog with us?

@Juriyx
Copy link

Juriyx commented Mar 28, 2022

If it works locally but not on Azure DevOps then there must be something different in these environments

Yes. It is OS - macOS 11 on Azure DevOps and Windows 10 locally. But I tried windows-2019 and it doesn't change the situation.

this example works for me with MAUI Preview 14:

I need to explain. There are 3 main folders when building Azure pipeline - sources (s), build (b) and artifacts (a). Sources contain repository files, build logically should contain files from build and artifacts - package files, copied from build folder. But instead of this everything goes into sources by default.
Therefore, when using the "publish" command I would like to get build files in "b" and artifacts in "a" but the "a" folder remains empty. This happens on DevOps. Locally it works.

So the main question is: When using "publish", which correct parameters should I use to specify the folder for build and the output folder for artifacts?

@jonathanpeppers
Copy link
Member

@Juriyx it sounds like you need to share a sample project with this problem. You are setting MSBuild properties like $(OutputPath) or $(IntermediateOutputPath) to custom locations?

Is a dotnet new maui template working for you as-is?

@Juriyx
Copy link

Juriyx commented Mar 29, 2022

You are setting MSBuild properties like $(OutputPath) or $(IntermediateOutputPath) to custom locations?

Yes, I set them to custom locations (/p:OutDir and /p:PublishDir)

Is a dotnet new maui template working for you as-is?

I will check it in DevOps. Locally the same problem appeared but I investigated that it was a mistake in letters in the wrong keyboard layout. After the fix, it worked as expected.

@Juriyx
Copy link

Juriyx commented Mar 30, 2022

Is a dotnet new maui template working for you as-is?

Hi. I tested the clean template in the console locally and the publishing path didn't work. Here is the binlog:
MAUIandroid.zip

@jonathanpeppers
Copy link
Member

@Juriyx is this problem happening because this is an "incremental build"?

Skipping target "_Sign" because all output files are up-to-date with respect to the input files.

Then later this target isn't finding any output files:

image

What should be happening here is any .apk or .aab files should be added to ResolvedFileToPublish, and it looks like they don't exist.

Are you doing a build then publish in a second step? If you clean first, does the problem go away?

@xperiandri
Copy link

@jonathanpeppers we call publish right after restore. Without build
Because we cannot publish with --no-build option. It just fails.

@jonathanpeppers
Copy link
Member

So these were my steps that worked before:

dotnet new maui
dotnet publish -c Release -f net6.0-android -o test

You're doing the same?

@jonathanpeppers
Copy link
Member

This seems to work for me, too...

dotnet new maui
dotnet build -c Release -f net6.0-android
dotnet publish -c Release -f net6.0-android -o test

@xperiandri
Copy link

xperiandri commented Mar 30, 2022

No, because as we explained we want to have binaries in /b folder but artifacts in /a folder.
And before that we manually build our dependencies and their binaries also go to folder /b
Like this dotnet publish --no-restore /p:OutDir="C:\Temp\B Android" /p:PublishDir="C:\Temp\A Android" /p:BuildProjectReferences=false

@xperiandri
Copy link

xperiandri commented Mar 30, 2022

We have common projects for backend and mobile that can be versioned themself and mobile and server parts that are versioned themself.
So first I build all common projects with their version.
Then I build mobile head and F# logic projects with their own version skipping common projects. As long as they are already built and present in folder /b

@danielchalmers
Copy link
Contributor

danielchalmers commented Apr 2, 2022

Is there an alternative to -f:net6.0-android that uses the version already specified in the project file?

Like -f:net-android or --arch android or something?

@jfversluis
Copy link
Member Author

I think we have established that this works and we have official docs now for it. So closing this one for now. If you have any specific issues with publishing, please open an issue for that.

Thanks everyone for the input!

Repository owner moved this from Todo to Done in [ARCHIVED] MAUI Planning Apr 15, 2022
@xperiandri
Copy link

dotnet/android#6932

@xperiandri
Copy link

dotnet/android#6933

@ghost ghost locked as resolved and limited conversation to collaborators May 16, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area-publishing Issues with the app packaging/publishing process (ipk/apk/msix/trimming) platform/android 🤖
Projects
No open projects
Status: Done
Development

No branches or pull requests

6 participants