Skip to main content

Command Palette

Search for a command to run...

My first Unison Cloud service

Updated
5 min read
My first Unison Cloud service
E

Software engineer at the intersection of functional programming, product development and open-source

I already mentioned on this blog how amazing it feels to program with Unison. Since I started using Unison, I have created several libraries, but I never deployed and used a Unison Cloud service that wasn't just an experiment. Not anymore!

Scratching an itch

When you create Unison programs, your code is type-checked and then saved to a database. You don’t exchange files with others but use Unison terms. Revolutionary. Perhaps a bit too much, since a full ecosystem had to be built from scratch to support this approach. GitHub becomes unusable. In its place, there is Unison Share where you can browse projects and collaborate with others. Unfortunately, Github is the portfolio of many developers, and as a Unison developer, all your frantic activity is invisible to most of the development world.

Fortunately Unison Share provides an API and a system of webhooks. You can install webhooks that will be notified every time an event related to your project occurs: a new push, a new ticket, etc… Hence the straightforward idea to use a webhook to mirror the Unison Share project activity to a Github repository.

Designing it

The most direct way to implement this system is to deploy an HTTP server that will listen to webhook payloads and create commits to a Github repository. This is exactly what Unison Cloud supports out of the box!

However, the details matter. At first, I wanted to deploy and operate a service that any Unison developer could use. They would register, configure their webhooks to send payloads to my server, and I would create commits. But this requires trusting me with a very sensitive resource, their Github API token. After looking carefully into the configuration of Github tokens, I concluded that there was no way to make the system trustworthy. Instead of me deploying a service and operating for multiple users, I preferred to let everyone start their own server with their own secrets. It’s a bit sad because I was looking forward to operating a Unison service supporting many users.

The whole system is eventually composed of:

  • A HTTP service listening to webhook payloads.

A CLI application to:

  • Deploy the service.

  • Synchronize a Unison Share project and a Github repository (or all of your projects at once).

  • Get some status information.

You notice that the CLI gives you the possibility to deploy the service with one command. This is, after all, one of the strength of Unison Cloud, the deployment code is still Unison code!

Implementing it

That was actually the fun part! I have, for the past year, created a few Unison libraries and this was finally the opportunity to use many of them in the same project:

  • potions a CLI library.

  • registry a dependency injection library.

  • json-encoder / json-decoder two libraries to create encoders / decoders for a given data model.

  • to-text a library to create flexible ToText instances for a data model.

Since eating your own dog food is the best way to improve your projects, I made a few improvements and fixes:

  • potions

    • Improv the display of enum flags.

    • Add the possibility to specify command name aliases.

    • Fix the missing item in a numbered list.

    • Better error message in the case of an incorrect subcommand.

    • Render colored text as Word and not Paragraph.

    • Add grey color style.

    • Upgrade the terminus library (I found an issue with the latest release in the process which was promptly fixed by Runàr)

  • registry

    • Expand the API for overriding components (setValueFrom, overrideValue but I’m still not satisfied with this API).

    • Add a makeDiagram function to produce a Mermaid diagram for an application.

Talking about that last feature, I can now display a diagram of the application!

The diagram is directly derived from the declaration of the components of the application (and pasted to Mermaid Live).

This diagram reveals some interesting points about the use of dependency injection:

  • An HttpClient gets an AuthorizationToken that is automatically provided to make queries to both Unison Share and Github.

  • However this token must be different when the HttpClient is used by the ProjectApi than when it is used by the GithubApi, and the registry library supports this use case.

  • Since the Application uses APIs to communicate with other systems, it is possible to implement in-memory versions of those APIs to write purely in-memory tests for the Application. There are still some integration tests against those APIs, actually creating / deleting repositories for example, to check that everything works ok.

  • You can also notice a difference between the Console and the Logger. The Console is used to display user messages on screen (with the help of the excellent terminus library) while the Logger is only activated on demand or when there are errors.

Using it

Finally, I can use the application for my own needs! I used tiles deploy to deploy the service:

I can even tail it directly from the Unison Command Manager, ucm:

Now, let’s have a look at all the synchronized projects:

They are all synchronized but you will notice that some of them are private. In that case the corresponding Github repository, created when using the sync command, is private too.

Finally, what does it look like on Github?

For now I’m only creating one commit per push, with the full payload updating the README file of the project.

I hope to be able to use the newly announced history comments of ucm-0.5.50 to produce specific commit messages when possible.

What’s next?

This was the perfect chance to create and deploy a simple Unison service. Next, I want to deploy something that will attract more users, include some storage, and offer meaningful analytics so I can enjoy more of the Unison ecosystem. 😊