Wednesday, September 30, 2020

GanttProject 3.0 packaging and update system

TL;DR: there are many somewhat technical details below. Long story short, GanttProject 3.0 comes with built-in update system. Leave it enabled to get bugfixes and other minor updates faster.

Some 10 years ago GanttProject distro weighed about 10 Mb, in ZIP archive. Installers for Windows and macOS packages contained, basically, the same ZIP distro and platform-specific executable launcher which searched for pre-installed Java Runtime on user's computer. It was easy to install Java Runtime and sometimes Java was even bundled with the operating system.

La belle époque! In 2020 Java software vendors can't rely anymore on Java Runtime availability on the user's computer. There are many technical reasons for that, but let's skip them for brevity. In short, the only reliable way to find suitable Java Runtime on the user's computer is to bring your own one. 

So, GanttProject 3.0 for Windows and macOS comes with bundled Java Runtime. It weighs about 100 megabytes.  Packages, particularly on macOS, require boring manipulations with signing and notarizing (whatever it means), otherwise the only thing that user is allowed to do with your app is to move it to Trash folder. Building and testing a single release may easily take a day or two. We're fine with doing it once per few months, but it is not really tempting to do it every week. On the other side, we'd like to be able to iterate quickly and publish updates frequently.

That's why we implemented our own simple update system in GanttProject 3.0.

User interface informs about the availability of new updates.

GanttProject since version 2.0 internally was built from a few modules which are managed by a small kernel code. The modules are just folders on disk with some zipped libraries and XML files inside. The kernel scans a couple of known folders, detects modules and runs code from them. 

In GP 3.0 we added versions to modules and now when the kernel finds two modules with the same id, but different versions, it takes the one with the highest version number. Our modules now look like a sort of stacked layers.

An example of the base version 2906 with some updates up to 2910

This rather simple modification turned out very powerful. We can build a new version of a single module put it next to the existing versions and -- voilà! -- when GP launches, it uses the new version. We can remove a few latest versions from the disk and thus rollback to some previous version. 

From the user perspective, when GanttProject 3.0 is starting, it contacts our download server, downloads a list of updates, compares it with what is already installed and, should it find a new update, asks the user if they want to install it. 

There is no hidden automated installation or downloading the updates in the background. It is relatively easy to roll back. We can fix a bug and deliver it to the users in the matter of hours, and it actually happened in the latest build 2910. A sad issue with CSV export and import on some locales where decimal separator is different from dot (e.g. comma) was fixed and published about 7 hours after reporting. 

We plan to use these updates to deliver bugfixes, localization and calendar updates just like we did with the minor releases in GanttProject 2.X series. Major updates will come in a full distribution.

Download GanttProject 3.0, let it update to the latest build and have fun!