Using Renovate Bot in Azure DevOps

|

I have been spoiled by the dependabot on GitHub, which helps keeping NuGet and other packages up to date. However, dependabot is not easily available in Azure DevOps. Again, the Open Source Community to the rescue! After asking around on social media, my friends Martin Björkström, Mattias Karlsson and Pascal Berger let me know of the existence of Renovate bot. The purpose of this bot is to periodically to update the dependencies that you use in your projects. It has loads of plugins for all sorts of package systems, like NPM, NuGet, PIP and many more. Probably, anything you think of, it has support for it or it can be configured to work with it.

Pascal conveniently let me know of a Docker image you can use in your pipelines to run renovate. This docker image comes with the packages pre-installed, such that you just need to execute renovate. This is nice, because then you do not need to install the renovate npm package on every pipeline run.

Configuration

To configure renovate, you will want to create a config.js file, here you can add stuff like private NuGet feeds, rules about which labels to apply on PRs and much more. For my usage, I need access too a private NuGet feed, and want to apply a label dependencies and a work item on every PR that renovate creates:

module.exports = {
  hostRules: [
    {
      hostType: 'nuget',
      matchHost: 'https://pkgs.dev.azure.com/<org-name>/',
      username: 'user',
      password: process.env.NUGET_TOKEN
    }
  ],
  repositories: ['<project>/<repository>'],
  azureWorkItemId: 12345,
  labels: ['dependencies']
};

For private NuGet feeds, you need to add hostRules, to let renovate know how to authenticate with the NuGet feed. For Azure DevOps Artifacts, you can unfortunately not just use the System.AccessToken in the pipeline, so you need to create a Personal Access Token (PAT), with permission to read the package feed.

You can have renovate create PRs for one or more repositories, provide a list of repositories you want it to run on. You can quickly deduct this from the URL for your repo, which will be in the format: https://dev.azure.com/<organization>/<project>/_git/<repository>. Each repository you want to be scanned you add like: <project>/<repository>.

On my repositories, I have branch protection enabled and have a rule that work items must be linked to each PR. So for this I have created a work item, which I simply use for each renovate bot Pull Request.

That is it for the configuration.

Pipeline definition

With the configuration in place, you can now set up a pipeline to run renovate based on a schedule. I have used the example renovate suggest, running every night at 3.

This pipeline is using the docker container Pascal Berger let me know exists. So every step after specifying container will run inside of that.

The env argument NUGET_TOKEN, is what the password for the hostRule for the NuGet feed above will be replaced with. In my case it is a Personal Access Token (PAT) that only has access to the private NuGet feed. The GITHUB_COM_TOKEN is used to get release notes for Pull Request descriptions when renovate creates such.

schedules:
- cron: '0 3 * * *'
  displayName: 'Every day at 3am (UTC)'
  branches:
    include: [develop]
  always: true

trigger: none

pool:
  vmImage: 'ubuntu-latest'

container: swissgrc/azure-pipelines-renovate:latest

steps:
- bash: |
    npx renovate
  env:
    NUGET_TOKEN: $(NUGET_PAT)
    GITHUB_COM_TOKEN: $(GITHUB_TOKEN)
    RENOVATE_PLATFORM: azure
    RENOVATE_ENDPOINT: $(System.CollectionUri)
    RENOVATE_TOKEN: $(System.AccessToken)

With this, you should be good to go! First time renovate runs, it will create a pull request with a renovate.json file. Merge it and it will from now on create Pull Requests with dependency updates! Neat!

Here is a screenshot of how this looks.

pr

This works in many environments. Refer to the renovate documentation for more info.

Migration of Xamarin Libraries and Apps to .NET 6.0/7.0

|

We are well into .NET6 and .NET7 release lifecycle and .NET8 coming up soon. Additionally end of life of Xamarin is coming increasingly closer, so I bet a lot of people are looking towards migrating to the newer bits.

I have already migrated every App I work on to NET7.0 and have done multiple releases to stores of these Apps. So wanted to share some insights with everyone.

1. Breaking changes

You maybe we aware that net{6|7|8}.0-ios targets are incompatible with Xamarin.iOS targets. The breaking changes primarily are how NFloat and related types are now handled. Instead of living directly in .NET 6.0, these types are now added implicitly using the NuGet package System.Runtime.InteropServices.NFloat.Internal. If you are already using .NET 6.0 you might have noticed this package gets pulled in, even though you have no other Package References. This makes Xamarin.iOS assemblies not forward compatible with the newer target frameworks. Hence, library authors need to migrate their projects to make them compatible.

I have done a couple of migrations, a huge one in MvvmCross which has a lot of target frameworks it targets. Migrated NukeProxy which is a iOS Binding Library, which had to be converted to using XCFramework instead of a Fat library. I have migrated a bunch of internal libraries and 6 Apps at work. So now I have a little bit of experience behind the belt.

2. Prerequisites

Make sure your .NET version is up to date. As of November .NET 7.0 is out and newer versions of Visual Studio for Mac will pull this the things described here should also be working if even if you are on .NET8.0 already, but also if you are for some reason on .NET6.0.

The bits for Android, iOS, macOS, mac catalyst etc. are no longer distributed with Visual Studio. Instead you will need to use a new feature in .NET to install a “workload” for each of these. This is super easy and much nicer in my opinion, instead of having to open a UI tool you can easily update and install these tools in the command-line. For instance if you want Android and iOS you do:

dotnet workload install android ios

You will need to install the appropriate workload for each Target Framework you intend to support in your library. If you are going to use .NET MAUI, then you might also want to install the maui maui-android maui-ios workloads.

3. Migrating a project to new SDK style project

The new SDK style project has been available to use for a very long time. The biggest advantage of these new project styles, is that you will not have to specify every single file in your project file that you want to compile. Instead the new style just picks up any know file types and makes some sane assumptions about the build actions for the files and adds them automatically. This makes working with csproj files much easier, as they are not polluted with loads of <Compile Include="folder/myfile.cs" /> definitions all over the place. The only things you would really have in these new csproj files is defining Target Framework, Package References and Project References. If you have other stuff, you may be doing something wrong.

If you compare the Playground.iOS csproj file in MvvmCross, it went from over 200 lines of code to 29, since the new SDK style projects are so more succinct.

The easiest way to migrate to the new project style and new TFM, is simply creating a new project and dragging over all your files, then adding all the project references and package references.

If you need inspiration of how a csproj file looks you can have some inspiration from some of the MvvmCross project files.

Android Library: MvvmCross RecyclerView csproj

Multi-target Library: MvvmCross Messenger Plugin csproj

4. Do I still need MSBuild.SDK.Extras for multi-targeting?

In MvvmCross I historically used the excellent MSBuild.SDK.Extras project to help with multi-targeting scenarios. However, after migrating projects to net6.0 I started having weird build issues. Migrating away from MSBuild.SDK.Extras resolved my issues. Your milage may vary, but it has helped me with a bunch of issues not to use it anymore.

If you are reading this and don’t know what it is, then you are not missing out on anything. It was just necessary with the older target frameworks to have a bunch of extra setup, which excellent community members such as Claire Novotny helped making for library authors to have a much nicer experience. However, it appears this is not necessary to use anymore.

5. Using Xamarin.Essentials?

If you are using Xamarin.Essentials, you may have heard that this is now moved over to MAUI.Essentials. However, not everyone are making Apps using MAUI, so you don’t really want to pull in everything from MAUI just to have access to the MAUI.Essentials API. MAUI.Essentials is not a NuGet package you pull in though. As of writing this post you can add MAUI.Essentials, by adding the following to your csproj file in a <PropertyGroup>:

<UseMauiEssentials>true</UseMauiEssentials>

Remember to initialize MAUI essentials on startup in your Activity or Application or View controller:

Microsoft.Maui.ApplicationModel.Platform.Init(this);

Read more about the migration in the Microsoft Docs

6. Change IntPtr to NativeHandle on iOS

Often when you are creating CollectionView or TableView cells among other views on iOS, you need to add constructors which historically used IntPtr in the constructor. This has changed now and you need to switch all these over to use NativeHandle or you will encounter issues at runtime, where it will complain about missing constructors.

7. My App crashes with PlatformNotSupported exception in System.Linq.Expression errors at runtime

There are places in net6.0 and net7.0 where some paths use code that requires JIT compilation. Such as when using parts of System.Linq.Expression which internally uses System.Reflection.Emit. This is supposed to be fixed in net8.0.

Read more here:

If you encounter such issues you will have to add the following to your iOS project:

<UseInterpreter>true</UseInterpreter>

You may also want to experiment with adding

<MtouchInterpreter>-all</MtouchInterpreter>

You can read more about this in Ryan Davis’s excellent blog post about improving build times

But UseInterpreter kicks in the Interpreter mode for your App and allows for some cool features, which are also described by Ryan Davis in his post about the mono interpreter. Among these emitting code, patching code at runtime and more, but more importantly fixes runtime crashes until libraries get patched.

8. I have a Binding Library what do I do with that?

Business as usual. However, I’ve found that switching over to output XCFramework libraries for the stuff you want to bind, is much easier to work with. Especially if you also want to support Mac Catalyst, then it is a must.

You still need to provide a ApiDefinition and Structs file. Specifically for these you need specific build actions in your csproj file:

<ItemGroup>
  <ObjcBindingApiDefinition Include="ApiDefinition.cs" />
  <ObjcBindingCoreSource Include="Structs.cs" />
</ItemGroup>

Then you can refer to your XCFrameworks like so:

<ItemGroup>
  <NativeReference Include="..\..\Output\NukeProxy.xcframework">
    <Kind>Framework</Kind>
    <SmartLink>False</SmartLink>
  </NativeReference>
  <NativeReference Include="..\..\Carthage\Build\Nuke.xcframework">
    <Kind>Framework</Kind>
    <SmartLink>False</SmartLink>
  </NativeReference>
</ItemGroup>

If you are still on .net6.0, you may have issues with BitCode being pulled in for some reason. Refer to this comment on GitHub to see how you can add extra stuff in your csproj to get rid of it. Supposedly it is fixed in .net7.0.

I think this is more or less what you need to know. Sure, this is not a complete migration guide, so you will have to try this yourself, but hopefully there are some things here that might you. If you have any questions, please do reach out on Discord, Mastodon, Twitter or in the comments below.

Easy Setup of Dev Tools on macOS with ZSH dotfiles

|

I recently got a new machine at work. Before I got it I spent a bit of time preparing and figuring out which essential tools I need to do my daily work. But, also to minimize the amount of time I would have to spend installing everything.

As you the reader might know I develop mobile Apps with Xamarin and .NET. So this blog post will be geared towards that. However, you would be able to install anything using the same approach.

My colleague pointed out that there is this super cool feature of ZSH called dotfiles. ZSH ships with macOS ans is the shell that you see when you open a terminal. It has a set of very powerful interactive tools but is also a script interpreter. Similar to other shells you might know such as bash, fish and csh.

The configuration for ZSH happens with a file called .zshrc, which is a list of settings, aliases for commands, styles and more.

Dotfiles in ZSH is a way to organize the settings you would normally put it .zshrc, but not only that, you can organize other configuration files for other tools, and also organize scripts.

There are multiple attempts to build on top of these dotfiles for easily managing the tools you need for your daily work. The version I went for is Oh Your dotfiles. With this tool you can make a logical folder structure with scripts and dependencies you want to have installed.

Let’s dig into it!

Start off by cloning Oh Your dotfiles:

git clone https://github.com/DanielThomas/oh-your-dotfiles ~/.oh-your-dotfiles

Then you run

ZDOTDIR=~/.oh-your-dotfiles zsh

Followed by:

dotfiles_install

Now you have oh your dotfiles installed. Time to create your own repository to customize your installation. For my own use I’ve created a dotfiles repository, with the tools I need. I’ve called it dotfiles on purpose as Oh Your Dotfiles automatically looks for paths containing dotfiles in the name, call it anything you want, just make sure you clone it into a folder starting with . and containing dotfiles in your user directory.

Now with the stuff cloned, you can start add content.

For instance. I have a folder called dev with a script called install.homebrew with the following contents:

ca-certificates
git
gh
scrcpy
[email protected]
mitmproxy
gawk
imagemagick
curl
wget
tree

This will install all those tools from homebrew.

Similarly if you want to install stuff from homebrew casks you can create a install.homebrew-cask file and add stuff there. For instance I have this in one of my folders:

firefox
amethyst
anydesk
little-snitch
spotify
gimp
signal
macfuse
keka
hot

When you’ve added some files and stuff to download. Simply run dotfiles_update and it will install everything.

Remember to synchronize your dotfiles somewhere. Then next time you get a new machine you can simply do:

git clone https://github.com/DanielThomas/oh-your-dotfiles ~/.oh-your-dotfiles
git clone https://github.com/Cheesebaron/dotfiles ~/.dotfiles
ZDOTDIR=~/.oh-your-dotfiles zsh
dotfiles_install

Then you can wait and everything is installed. Super convenient.

Using Android Tiramisu Preview Workloads in Azure Pipelines

|

Had the pleasure of hitting a bug in the net6.0-android TFM when targeting Android 31. This bug had been fixed in a preview version of the net6.0-android workload, targeting Android 32 or higher.

So question is, how would you set up your CI or even your local environment to run this?

In my specific case I wanted:

  • Android platform for android-Tiramisu
  • .NET workload for android, containing the fix, android-33

The steps to install these are fairly simple. Use Android SDK manager to install the platform. Then use dotnet to install the specific workload.

sdkmanager --install "platforms;android-Tiramisu"

dotnet workload install android-33

This enables me to use net6.0-android33.0 as TFM.

sdkmanager is located in your Android SDK folder to invoke it, you might need to add it to your path or cd into the folder <android-sdk>/cmdline-tools/latest/bin and run ./sdkmanager.

Setting up steps in Azure Pipelines is super easy too:

- script: |
    ${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin/sdkmanager --install "platforms;android-Tiramisu"
  displayName: Install Android SDK for Tiramisu

- task: UseDotNet@2
  displayName: 'Use .NET Core SDK 6.0.x'
  inputs:
    version: 6.0.x

- script: |
    dotnet workload install android
    dotnet workload install android-33
  displayName: Install Android Workloads

Now you should be able to build .net6.0-android33.0 TFMs!

Using Charles Proxy with Xamarin.Android

|

I’ve been debugging some network issues in one of my Apps recently. To help me I acquired the help of the excellent macOS Application Charles Proxy. I needed to see what was sent to and from the servers the App communicates with and also check the contents. One issue though, all the calls are through SSL, so without a little bit of setup, you will not get far checking the contents of the calls.

The information served in this blog post, may also work with Fiddler and mitm-proxy if you prefer those tools.

Charles Proxy has a way to do SSL proxying, which can show you the text contents of SSL requests and responses. You can specify specific sites to include. To get this to work with Xamarin.Android or .NET6 Android, we need to tell the AndroidClientHandler which certificates we trust. In addition to the regular set up specifying Charles to be the proxy in the WiFi Settings.

For some reason using the built in Android functionality specific to setting networkSecurityConfig in the Android manifest and allowing to use trusted user certificates doesn’t work with AndroidClientHandler, so we have to do some manual grunt work.

1. Setting Up Device Proxy

First we need to modify the device settings to point towards Charles Proxy. You can do that through WiFi settings on your device. Go to Settings -> Network & Internet -> Wi-Fi. Press the Gear icon next to the connected network and edit it using the pencil icon.

In the Proxy drop down select Manual

Proxy hostname will be the IP of the machine you are running Charles Proxy on. You can find this using Charles in the Help -> Local IP address menu. Proxy port will be the port defined in Charles Proxy settings, default is 8888

Screenshot of Proxy Setup for Charles

Doing this step, you should start seeing traffic flowing into Charles from your device. You will see that all SSL requests/responses have garbled data. Not something you can read. We will fix that in next step.

You can also verify the proxy works by going to http://chls.pro/ssl in a browser on the device, and it will download the Charles Root Certificate. For this setup, it is optional to install it.

2. Adding Trusted Certificates to AndroidClientHandler

Since AndroidClientHandler doesn’t grab user installed certificates, we need to add them ourselves.

In Charles Proxy go to Help -> SSL Proxying -> Save Charles Root Certificate…. Save it as Binary certificate (.cer).

In your Android project add it to the Assets folder and remember to mark the file Build Action as AndroidAsset.

Now to setting up a AndroidClientHandler with the certificate.

private AndroidClientHandler CreateClientHandler()
{
    CertificateFactory? factory = CertificateFactory.GetInstance("X.509");
    var clientHandler = new AndroidClientHandler();
    if (factory == null)
        return clientHandler;

    try
    {
        using Stream? stream = ApplicationContext?.Assets?.Open("charles-ssl-proxying.cer");
        var cert = (X509Certificate?)factory.GenerateCertificate(stream);

        if (cert != null)
        {
            clientHandler.TrustedCerts ??= new List<Certificate>();
            clientHandler.TrustedCerts.Add(cert);
        }

        return clientHandler;
    }
    catch (Exception e)
    {
        _logger.LogError(e, "Failed to get Charles Certificate");
        throw;
    }
}

Basically what happens here is that we load the charles-ssl-proxying certificate from assets and create a certificate that we add to AndroidClientHandler.TrustedCerts. This is where it expects extra certificates to be added.

Now with this AndroidClientHandler you can create your HttpClient with that handler:

var client = new HttpClient(CreateClientHandler());

In my latest project I started using HttpClientFactory, which allows me to set this handler once and forget about it.

Anyways, when creating requests with HttpClient now, you should be albe to see the data sent and received even when using SSL!

Note: remember to add domains to the SSL Proxy Settings in Proxy -> SSL Proxy Settings in Charles. A good practice is to limit these to the domains you expect and want to debug.

Screenshot of Charles SSL Proxy