Creating a Signed and ZipAligned APK (for Google Play) from Xamarin

Posted on November 22, 2016

Introduction

This is a guest post by Cedd Burge, Software Developer Lead at RES.

This post is written from the point of view of someone (me) who is already proficient in C#, but was new to Xamarin, Mobile phone development, and AppVeyor.

It contains from scratch steps to create a Xamarin Android application, to build it on AppVeyor and to publish it to the Play Store. You can look at the repo I created to test this post if you get stuck.

I am developing on Windows, so these steps probably won’t work if you are developing on a Mac.

First, install Xamarin from https://www.xamarin.com/download.

Create a new repository on GitHub

If you are new to GitHub, see this getting started guide, otherwise simply create a new repo and git clone it somewhere convenient.

Create a new Xamarin Portable Class Library (PCL) project

In my version of Visual Studio (Community 2015), this is done by clicking “File - New - Project” on the main menu and then selecting “Blank App (Xamarin.Forms Portable)” from “Templates - Visual C# - Cross-Platform”. Give it a interesting name, which I will assume to be YourAppName for the rest of this post.

Run the app!

Select the YourAppName.Droid project and run it. This should show the bare bones app in an emulator.

If you have Hyper-V enabled (maybe you use Docker), then you might get an Deployment Error when doing this. Disable Hyper-V and restart your machine to fix this.

You might also run in to compile errors due to ridiculous dependency weirdness.

Create APK file manually

To tell Google about your app, you have to make some changes to the Properties\AndroidManifest.xml file of the YourAppName.Droid project.

  • Add a package="com.yourappname" (or similar) attribute to the root manifest node. The package name must be unique on Google Play and must follow normal java package name conventions. Most people use their url in reverse (eg com.yourappname instead of yourappname.com) and stick to lower case.
  • Add an android:versionCode="1" attribute to the root manifest node. This is an integer and it must be incremented every time you upload an apk on Google Play.
  • Add an android:versionName="0.1 attribute to the root manifest node. This value can be anything you like and is displayed in Google Play.
  • Change the label attribute on the application node to YourAppName.

Visual studio has some tools to create an APK, and they seem to be in constant churn, but at the time of writing, the process is as follows.

  • Change the “Build Configuration” to “App Store”.
  • Select the YourAppName.droid project and click “Build - Archive” from the main menu.
  • The archive manager window will appear and build your app.
  • Click “Distribute”, which will pop up the Distribute window.
  • Initially you won’t have a signing identity, so click on the green plus button and fill in the details to create one.
  • Once created, double click on it and note where it is on disk (YourKeyStoreFilename from now on)
  • Click “Save As” to create an APK.

Upload the APK to Google Play

  • Create a Developer Account on Google Play Developer Console (this costs $25)
  • Click “Add New Application” and add YourAppName
  • Upload the APK that you saved in the previous step

Publish the App on Google Play (probably just to Alpha or Beta)

There are some requirements when publishing an application to Google Play, and these are likely to change, but happily google tells you what they all are. If you click on “Why can’t I publish?”, near the top right corner of the page, you will get a list of things to do.

It’s all simple stuff that can be done within the Developer Console. Some screenshots and pictures are required. If you just want to get a test version up quickly, then feel free to use mine temporarily.

There are a lot of optional things you can do as well, which can be worthwhile if you want to publish a killer app. The Google Launch Checklist, is comprehensive, but takes a long time to read.

Automate APL creation locally

When working with appveyor, it always makes sense to test on your own computer first. The feedback is immediate and you iterate very quickly. It takes a lot longer to modify the appveyor.yml file, push it and wait for a build to go through. Also, if it works locally but doesn’t work on AppVeyor, you know the problem is a configuration difference between your computer and the AppVeyor environment (eg a different version of msbuild).

Being as we are making a new version of the apk, we need to increment android:versionCode in Properties/AndroidManifest.xml.

There are some Xamarin MSBuild targets, which we can use to create a Signed and ZipAligned apk as below.

There are 2 passwords required in the command because java KeyStores can contain multiple Alias’. So the first password is to access the KeyStore, and the second one is to access the specific alias. Visual studio hides this complexity from you and assigns the same password to both places.

I do a lot of work in GIT Bash, but this statement only works in Batch (the windows command line), I think because of parameter escaping.

MSBuild "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" "/p:AndroidSigningKeyAlias=YourKeyAlias" "/p:AndroidSigningKeyPass=YourKeyStorePassword" "/p:AndroidSigningKeyStore=YourKeyStoreFilename" "/p:AndroidSigningStorePass=YourKeyStorePassword"  "YourAppName.csproj"

This will create com.yourappname-Signed.apk in the bin\release folder. Upload this to Google Play to make sure that everything is working properly.

Automate APK creation on AppVeyor

You will need to link an AppVeyor account to your GitHub one, so let’s do that:

  • Navigate to your repo in GitHub
  • Click “Settings” on the repo
  • Click “Integrations and services”
  • Click “Browse Directory”
  • Click “AppVeyor”
  • Click “Configure”
  • Click “Grant Access”

Now Log in to AppVeyor.com, probably using your GitHub account

  • Click “Projects”
  • Click “New Project”
  • Choose your GitHub repository and click “Add”

MSBuild needs to access your KeyStore file in order to sign the apk, so copy YourKeyStoreFilename in to the folder of YourAppName.Droid project (called YourKeyStoreLocalFilename from now on).

When we created the apk from the command line, we entered in some passwords, and we obviously can’t save these passwords to a public Git repository. Happily AppVeyor have thought of this, and you can convert passwords in to tokens that can be exposed publically.

To do this, click your user name in the top right hand corner and then click “Encrypt data” from the drop down menu. Enter YourKeyStorePassword in to “Value to encrypt” and click “Encrypt”. AppVeyor will then display a token which you can use in place of the real value.

Now that we have everything we need, add an appveyor.yml file to the root of your repository as below. Note that YourLocalKeyStoreFilename is relative to the csproj file being built (the YourAppName.Droid folder below).

environment:
 keystore-password:
  secure: DSwAr4fYt3Q35Sjob5qAN5uj # YourPassword for keystore
before_build:
 - nuget restore
build_script:
 - msbuild "/t:SignAndroidPackage" "/p:Configuration=Release" "/p:AndroidKeyStore=true" "/p:AndroidSigningKeyAlias=YourKeyAlias" "/p:AndroidSigningKeyPass=%keystore-password%" "/p:AndroidSigningKeyStore=YourLocalKeyStoreFilename" "/p:AndroidSigningStorePass=%keystore-password%"  "YourAppName.Droid\YourAppName.csproj"
artifacts:
 - path: YourAppName.Droid\bin\Release\com.yourappname-Signed.apk

Remember to update android:versionCode and then push to GitHub. This will trigger a build on AppVeyor. The build_script section will call msbuild to create the signed and zipaligned apk file, and the artifacts section will archive the apk file so we can download it later.

Go to AppVeyor.com, click on your project, click on “Artifacts”, download the apk file, and then upload it to google to check that everything has worked properly.

Conclusion

There is a lot to learn to get everything working, and I couldn’t find a single source for all of these things, but having done it once, the process is actually quite simple, and the tools and services involved are generally a pleasure to work with.

Best regards,
Cedd Burge

Follow Cedd on Twitter: @cuddlyburger
Follow AppVeyor on Twitter: @appveyor

AWS Elastic Beanstalk

Posted on November 07, 2016

Appveyor does not support AWS Elastic Beanstalk deployment out of the box right now. However it can be automated in Appveyor with help of some scripting. Here is small guide based on this support forum discussion.

  • In the root folder of your web application create text file named awsdeploy.txt.
  • Add the following to awsdeploy.txt:

    Template = ElasticBeanstalk
    Container.ApplicationHealthcheckPath = /healthcheck
    
  • Add AWSAccessKeyId as environment variable and AWSSecretKey as secure environment variable.
  • Set Package Web Applications for XCopy deployment in build stage.
  • Set the following as a deployment script:
$packageweb = $artifacts.values | Where-Object { $_.path -like '*WebApplication1.zip' }
$exe = "C:\Program Files (x86)\AWS Tools\Deployment Tool\awsdeploy.exe"
&$exe -r "-DDeploymentPackage=$($packageweb.path)" "-DEnvironment.Name=MyAppWeb-test123" "-DApplication.Name=MyAppWeb123" "-DRegion=eu-west-1" "-DAWSAccessKey=$env:AWSAccessKeyId" "-DAWSSecretKey=$env:AWSSecretKey" "C:\projects\WebApplication1\awsdeploy.txt"

Note that this script assumes that application was already deployed at least once to Beanstalk, otherwise you need to replace -r switch with -w for single first deployment.

Here is an example YAML (only relevant parts):

environment:
  AWSAccessKeyId: AKIAIODIUCY3ETD6TEST
  AWSSecretKey:
    secure: LEvzbXpiLkWVvswonFHnAYV9ZS6fEFL3wswjTcIQ6ZXC5j1nynd6N0Bs/VFtest
build:
  publish_wap_xcopy: true
deploy_script:
- ps: $packageweb = $artifacts.values | Where-Object { $_.path -like '*WebApplication1.zip' }
- ps: $exe = "C:\Program Files (x86)\AWS Tools\Deployment Tool\awsdeploy.exe"
- ps: &$exe -w "-DDeploymentPackage=$($packageweb.path)" "-DEnvironment.Name=MyAppWeb-test123" "-DApplication.Name=MyAppWeb123" "-DRegion=eu-west-1" "-DAWSAccessKey=$env:AWSAccessKeyId" "-DAWSSecretKey=$env:AWSSecretKey" "C:\projects\WebApplication1\awsdeploy.txt"

Here is example web application folder structure:

Directory of C:\Projects\WebApplication1

09/15/2016  02:48 AM    <DIR>          .
09/15/2016  02:48 AM    <DIR>          ..
07/17/2016  12:34 PM               505 .gitattributes
07/17/2016  12:34 PM             2,858 .gitignore
09/15/2016  02:40 AM                80 awsdeploy.txt
08/30/2016  03:22 PM    <DIR>          WebApplication1
07/21/2016  06:51 PM             1,012 WebApplication1.sln

Enjoy!

The new build cache

Posted on September 28, 2016

AppVeyor runs every build on a clean virtual machine. Virtual machine state is not preserved between builds which means every build downloads sources, installs NuGet packages, Node.js modules, Ruby gems or pulls dependencies. Build cache allows you to preserve contents of selected directories and files between project builds.

AppVeyor was the first hosted CI to introduce a build cache and over the time it became a very popular build tool and important infrastructure component. There were some limitations though, such as maximum cache entry size of 500 MB and intermittent save/restore lags due to ever changing networking conditions.

Increasing requirements from larger customers running builds with “heavy” dependencies made us to re-visit cache architecture and thus the new and updated build cache was born! The new build cache lives close to build worker VMs, it’s fast and offers virtually unlimited possibilities for scale and has lower update/restore times.

Cache size

With the introduction of the new cache we are also changing the way it’s metered.

The total size of build cache is limited per account and depends on the plan:

FreeBasicProPremium
1 GB1 GB5 GB20 GB

It’s a hard quota which means the build will fail while trying to upload cache item exceeding the quota. The maximum size of a single cache entry cannot be larger than the size of cache.

Cache speed vs size

The new cache uses 7z to compress/uncompress files before transferring them to the cache storage. We chose 7z over built-in .NET compression library because it’s generally faster, produces smaller archives and works with hidden files out-of-the-box.

While compressing cache item, by default AppVeyor uses 7z with zip algorithm and compression level 1 (“Fastest”) thus producing archive faster, but with larger size (-tzip -mx=1 args). However, you can change compression behavior of 7z by providing your own command line args in APPVEYOR_CACHE_ENTRY_ZIP_ARGS environment variable. For example, to enable LZMA compression method with the highest possible compression ratio set this variable to -t7z -m0=lzma -mx=9.

Availability

The new build cache is currently in beta. It’s automatically enabled for all new accounts.

We are rolling out new cache to existing accounts in batches while observing performance. If you want participate in beta sooner or noticed any issues with the build cache please let us know.

Best regards,
AppVeyor team

Follow us on Twitter: @appveyor

Migrating build environment to RackSpace

Posted on July 16, 2016

Dear customers,

We have great news! Today we moved all open-source and most private accounts to a new build environment hosted at RackSpace.

For open-source projects that means their builds will start and run faster and private projects may notice better performance as well.

Virtual machines in the new environment have 2 CPU cores and up to 4 GB of RAM. The new public IP address for that environment is 74.205.54.20 - you may need to update your firewalls.

We are still going to maintain Google Compute Engine (GCE) environment for selected accounts and as a backup build cloud. If your private builds were previously running on GCE they will remain there.

Some open-source projects may experience issues while building on a new environment. Due to implementation differences between GCE and Hyper-V platforms we have separate build worker images for GCE and Hyper-V environments, so there might be minor discrepancies in installed software. Please report any issues you notice - your help with fixing those issues is much appreciated.

We have a good reason for this move as some customers already reported 10x performance increase!

We would love to hear your feedback!

Best regards,
AppVeyor team

Follow us on Twitter: @appveyor

Deployment projects

Posted on November 04, 2015

Sometimes your deployment requirements cannot fit into AppVeyor built-in deployment providers such as FTP, Web Deploy, S3 and others, for example, you are deploying to Elastic Beanstalk, or you have to recycle application pool during the deployment.

You can write a script doing custom deployment job, however this script can be run only on build worker VM during the build. “Environments” do not support custom scripts and there is no “Script” provider. This is because “Environment” deployments run on a shared background worker severs where potentially insecure custom scripting is not allowed.

This article demonstrates how you can simulate “Script” environment with regular builds and deploy project artifacts to any environment with your own custom script.

Solution overview

The basic idea is having two AppVeyor projects:

  • Main project - this is your main project running tests and producing artifacts.
  • Deployment project - helper project downloading artifacts from specific build of “Main project” and deploying them with your custom script. Here, to “deploy” the build of “Main project” you could either manually run new build of deployment project from UI or use AppVeyor API.

Deployment project

The “core” of deployment project is PowerShell script downloading artifacts. The script uses AppVeyor API to find specific build and download its artifacts. Artifacts are downloaded to the root of build directory (%APPVEYOR_BUILD_FOLDER%).

The following environment variables must be set for script to work:

  • api_token - AppVeyor REST API authentication token. Can be found/generated on this page.
  • deploy_project - project slug to download artifacts from. Project slug can be seen in the project URL.
  • deploy_version - build version to deploy. If not specified artifacts from the most recent version will be downloaded.
  • deploy_artifact - file name or deployment name of artifact to download. If not specified all artifacts will be downloaded.

These environment variables can be set on Environment tab of deployment project settings or in appveyor.yml:

environment:
  api_token:
    secure: ABC123==
  deploy_project: my-web
  deploy_version: ''            # download artifacts from latest build if no version specified
  deploy_artifact: ''           # download all artifacts if empty

To download artifacts add the following PS script into “Before deploy” section of your project settings or before_deploy scripts of appveyor.yml:

before_deploy:
  - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/deploy.ps1'))

Your own deployment logic can be put under deploy_script section, for example:

deploy_script:
  - command stopping app pool
  - '"C:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe" -verb:sync -source:package="%webdeploy_package%" -dest:auto,ComputerName="%webdeploy_server%",UserName="%webdeploy_username%",Password="%webdeploy_password%",AuthType="Basic" -setParam:"IIS Web Application Name"="%webdeploy_site%" -allowUntrusted'
  - ...
  - command starting app pool

Add these to disable automatic build and test phases:

build: off
test: off

Setup notifications if required:

notifications:
  - provider: <provider_1>
    settings: ...

Complete appveyor.yml example:

environment:
  # AppVeyor API token for your account, project, version and artifact(s) to download
  api_token:
    secure: ABC123==
  deploy_project: my-web
  deploy_version: ''            # download artifacts from latest build if no version specified
  deploy_artifact: ''           # download all artifacts if empty

  # deployment-specific settings
  # we are going to deploy using Web Deploy, so...
  webdeploy_package: '%appveyor_build_folder%\MyWebApp.zip'
  webdeploy_server: https://test.scm.azurewebsites.net:443/msdeploy.axd?site=test
  webdeploy_site: test
  webdeploy_username: $test
  webdeploy_password:
    secure: AAABBBCCC123==

# download project artifacts
before_deploy:
  - ps: iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/deploy.ps1'))

# here is your own custom deployment code
deploy_script:
  - '"C:\Program Files (x86)\IIS\Microsoft Web Deploy V3\msdeploy.exe" -verb:sync -source:package="%webdeploy_package%" -dest:auto,ComputerName="%webdeploy_server%",UserName="%webdeploy_username%",Password="%webdeploy_password%",AuthType="Basic" -setParam:"IIS Web Application Name"="%webdeploy_site%" -allowUntrusted'

# notifications
notifications:
  - provider: <provider_1>
    settings: ...

# disable build and test pahses
build: off
test: off

deploy.yml

If main and deployment projects share the same repository (most probably they do) you can put YAML deployment settings to a separate deploy.yml file, next to appveyor.yml and then set “Custom configuration .yml file name” on General tab to deploy.yml. To disable automatic triggering of deployment project on webhook you can either remove its webhook from repository or set branch filter to some non-existent branch, for example:

branches:
  only:
    - deployment-project

Main project

Main project is the project creating application packages and uploading them to build artifacts. With artifacts in place you can call deployment project (either manually or via API) to download and deploy artifacts.

To start a new build of deployment project during the build use Start-AppveyorBuild cmdlet:

environment:
  api_token:
    secure: ABC123==

deploy_script:
  - ps: Start-AppveyorBuild -ApiKey $env:api_token -ProjectSlug deploy-project -EnvironmentVariables @{ "deploy_version" = $env:appveyor_build_version }

Enjoy!

Follow us on Twitter: @appveyor