5 minute read

I’ve been writing a lot of Rust code this year, partly because I used it for some university projects and also because I’ll start writing a master thesis about concurrency in Rust soon.

There are many things I like about Rust and its ecosystem. One of them is the great CLI tooling with Cargo. Cargo is Rust’s build, testing, packaging and crate publishing tool.

So far I haven’t seen anything comparable for Nextcloud. Historically, many of the apps use Makefiles for the dev setup and packing. While some more sophisticated methods have been developed (I wrote about a similar topic a year ago), every project has a different kind of script to do the setup and packaging. That means the workflow is anything but standardized. Moreover, Makefiles aren’t the right tool for this job.

Since Makefiles are part of each app’s version control system, changes to the workflow have to be applied to each app.

That combined, I tried to implement the concepts I liked in Cargo but missed in Nextcloud in a new tool called Krankerl. It’s a very simple command line tool that shall help developers manage, package, sign and publish their apps. The goal is to establish a standardized and reproducible workflow for Nextcloud app devs. While I’m happy if others find this tool useful, I primarily develop this for my own needs since I’m maintainer of a few apps.

A quick overview

Right now, those are the commands included in Krankerl 0.4:

Usage:
  krankerl enable
  krankerl disable
  krankerl init
  krankerl list apps <version>
  krankerl list categories
  krankerl login [--appstore | --github] <token>
  krankerl package
  krankerl publish (--nightly) <url>
  krankerl sign --package
  krankerl --version

Options:
  -h --help     Show this screen.
  --version     Show version.

Packaging

One of the key features of Krankerl is app packaging. This means, taking an app’s files and putting them into a .tar.gz. For very simple apps it isn’t really more than that. However, more complex apps will require some additional steps for the packaging. For that purpose, Krankerl uses a krankerl.toml configuration files that lets you specify commands to run before packaging.

Too keep things reproducible, the tool assumes git is used as version control system. Krankerl doesn’t change the state of the local app repository. Instead, it clones the current HEAD into a new directory, from where it starts the packaging process.

There’s an additional setting for exclude patterns. This allows to shrink the size of app archives by excluding git, testing and unnecessary files.

Note: The app will fall back to sensible configuration defaults if no krankerl.toml file is found.

Signature

Nextcloud apps distributed via the official app store are signed. Thus, before we can push a new release to the app store, we have to invoke OpenSSL and have it do its crypto magic. Historically, this was one of the commands of a typical Makefile. Krankerl has a simple command for it. It follows the convention of finding app keys in the ~/.nextcloud/certificates directory and thus locates your keys automatically by reading the app ID from appinfo/info.xml.

Note that Krankerl only creates a signature for the archive. Nextcloud also has got a code signature support where it basically generates a hash values for every file in the app directory and signs the JSON-encoded list of file hashes. I’ve tried to re-implement this feature in Rust but I’ve failed at the point where I realized PHP uses a JSON encoding different to the one used in the serde_json crate for Rust and thus the signatures are wrong.

Publishing

Currently, I manually publish app release in the following way:

  1. git tag the current commit
  2. push the tag to GitHub
  3. run make appstore
  4. open the GitHub releases page and upload the .tar.gz
  5. copy the .tar.gz URL
  6. open the publish page on the app store
  7. paste the archive URL
  8. go back to the terminal and copy the archive signature (generated by the Makefile script)
  9. paste the signature on the app store
  10. hit submit

Those are simple, but many steps I have to take every single time I want to publish an app release. Obviously, this quickly gets annoying, especially if you are maintainer of a bunch of apps.

The Nextcloud app store has a simple and well documented REST API.Thus it’s easily possible to automate the task of registering the app release. From the above steps, Krankerl can take over step 3, 6, 7, 8, 9 and 10. That means, the only manual tasks left are uploading the archive to GitHub. The rest is fully automated and thus can be done a lot faster.

More helpers

Since we’ve got a neat little helper for the CLI, I thought it wouldn’t hurt to have a few shortcuts to enhance the CLI experience. I find myself enabling/disabling apps a lot, thus I’ve added an enable and disable command that simply invokes the occ with the app ID extracted from the info.xml file.

Download and run

Thanks to Rust, all you need to run the tool is to download and execute it. It should just work without installing any dependencies, assuming it’s being used on 64bit Linux, which is what I’ve compiled it for.

The name

In case you wondered about the app’s (strange) name: the word Krankerl means tendril in Lower Austrian dialect. This is what they look like: Krankerl (tendril) I chose it in lack of a better name.

Feedback welcome

Though, as noted my goal isn’t to change every Nextcloud app developer’s workflow by introducing this tool, I’d be more than happy if others find this useful and we can further improve those workflows. If you have any further ideas, questions or problems, please let me know in the GitHub issue tracker!

Some personal ideas for future enhancements are

  • Automatically upload the archive to a GitHub release
  • Specify the test commands (e.g. karma, phpunit) in krankerl.toml and execute them with krankerl test
  • Detect local changes in the app repo and force developers to commit before packaging to keep things reproducible
  • Automatically add git tags and update the version number in info.xml

Update 2018-03-27: Replaced externally hosted image with embedded one.