Calamares, a Linux system installer used by a few dozen different distro’s, is hosted on GitHub. The source code and issues live there, and the website is run from GitHub pages. This post meanders around GitHub actions – things that happen in response to changes in a project – and how I built a Matrix-notification-thing for Calamares.

Calamares is not a KDE project – call it KDE-adjacent, I guess – so there is a bunch of slightly-different technology and topics than a KDE project would use on KDE Invent, which is a GitLab instance.

The project has two main repositories (calamares and calamares-extensions), and project participants are interested in notifications about two main things:

  • issues opened and closed
  • Continuous Integration builds (CI, after a push and nightly across more platforms)

GitHub Actions provides a (terrible, YAML-based) language for writing down things to do and when to do them. The Calamares project uses this to do notifications for the things we’re interested in. At some point, I think GitHub had specific integrations for IRC notification; since then Calamares has moved most of its messaging to Matrix and we had to re-jig the setup.

All I Wanted Was a Shell Script

Sending notifications to Matrix is actually pretty simple: anything that can do a HTTP POST request will do, which means that the Swiss-army-knife called curl is your best friend.

The Matrix API Documentation is pretty good (and curl-centric). I registered a new account called calamares-bot on the Matrix.org homeserver, and then went through the following steps:

  • Get an access token by following the steps in the Login section. This gives me a random string which I’ll call MATRIX_TOKEN.
  • Figuring out the room ID for #calamares:kde.org. I think I started up Element in a web browser to do that, since the “joining a room via an alias” documentation didn’t make sense to me – and all I have is a canonical name, I don’t even know if that counts as an alias. This is another random string, which I’ll call MATRIX_ROOM.
  • The two strings can be used to send messages to a room:
    curl -XPOST -d '{"msgtype":"m.text", "body":"hello"}' \
      "https://matrix.org/_matrix/client/r0/rooms/$MATRIX_ROOM/send/m.room.message?access_token=$MATRIX_TOKEN"
    

    Here, the documentation is pretty good again (copy-and-paste errors above are my fault).

So what I really want is to run that curl command in response to new issues and build results. It’s just a (one-line!) shell-script, how hard can it be?

Just One Shell Script

GitHub has an “actions marketplace” which encourages you to use arbitrary scripts from other users as actions. Those scripts run in the context of your own CI, along with whatever secrets you hand them. What could possibly go wrong?

The documentation encourages referring to the arbitrary scripts from third parties by the full hash of the corresponding Git commit. I suppose full hashes will mitigate most of the supply-chain attacks on actions, but I fully expect some popular action to be bought up and have a tag changed leading to CI compromises. There’s also script hardening documentation which is both terrifying and laughable (I can see that my own scripts are vulnerable; this is C++ level of footgunnery, provided via Javascript and YAML).

With that in mind, I set out to do-it-myself, in a repository controlled by the Calamares project. That way at best we can compromise our own CI and not somebody else’s.

Initially I had a shell script, in the Calamares repository itself, to do the notifications, but there’s a curiosity: actions run with no checkout of the repository, so to get the script I’d have to clone the repo first. For CI builds that makes sense, but not so much for notification of issues being opened or closed.

So I need my shell script, (remember, this is going to be one line which calls curl with suitable data) to live somewhere that doesn’t need to be checked out.

But They Wouldn’t Give It To Me

Regarding this section title, yes they would, but otherwise I’d miss the opportunity to misquote more bits of Suicidal Tendencies’ “Institutionalized”.

What’s not immediately obvious to my old-school shell-scripting mind is that GitHub’s “compound runners” can effectively be shell scripts, that they can live in a separate repository in subdirectories, and that they’re magically available.

Here is the YAML bla-bla that wraps my one-line shell invocation of curl. There’s input and output parameters being defined and then used in the script – the script being the value of the run key down at the bottom.

So at this point I have

  • a separate git repository,
  • containing a subdirectory,
  • with 25 lines of YAML directing one line of shell-code.

But it’s easy to call! All I need is 5 lines of YAML at the call site (e.g. a GitHub workflow) to invoke the action: one to name it, and 4 lines to pass the parameters in (the strings MATRIX_TOKEN and MATRIX_ROOM and the message to send).

Since the token and room strings can be used to send messages to a given (possibly-private) room as a particular user, they open up the possibility of impersonation; they need to be secret. The repository and organization Settings pages on GitHub have a tab where a secret string can be named and stored. Then the string is available to actions, but won’t be displayed or logged unless you do it deliberately.

.. I’ll probably get hit by a bus anyway

I’m not going to think about how many different VMs or docker instances or whatnot get spun up for the purposes of notification – it happens on GitHub’s Azure backend, all automagically. No wonder people mine crypto as part of their CI pipeline.

In overall terms of code-lengte / lines written, this is remarkably inefficient; the only thing gained is that I have a cargo-cult 5-liner for any workflow step that needs notifications (e.g. issues changed or CI run completed). If I need notifications from other parts of the Calamares project – who knows, there might be a Pull-Request-Notifier at some point if external participation picks up – then I’ll have saved a little time.

For now, though, color me unimpressed with the whole thing. It’s a sunk cost, and part of increasing inertia to help with lock-in on the platform (re-doing this for GitLab would mean changing all the bla-bla surrounding the one line curl invocation). The only upside is that I’m running my untrusted code, and not somebody else’s (and I run my code every day, anyway).

PS. You can use this action yourself; if you trust me (bambi-eyes) invoke the action directly; if you don’t, copy the action into a repository of your own. There’s also other, similar Matrix-notifiers for GitHub actions with more-or-less fancy features.