My first Unison Cloud service

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
ToTextinstances 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:
potionsImprov 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
Wordand notParagraph.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)
registryExpand the API for overriding components (
setValueFrom,overrideValuebut I’m still not satisfied with this API).Add a
makeDiagramfunction 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
HttpClientgets anAuthorizationTokenthat is automatically provided to make queries to both Unison Share and Github.However this token must be different when the
HttpClientis used by theProjectApithan when it is used by theGithubApi, and theregistrylibrary supports this use case.Since the
Applicationuses APIs to communicate with other systems, it is possible to implement in-memory versions of those APIs to write purely in-memory tests for theApplication. 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
Consoleand theLogger. TheConsoleis used to display user messages on screen (with the help of the excellentterminuslibrary) while theLoggeris 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. 😊



