Conveyor updates for March

The March Conveyor update makes it simple to define file associations for every OS, and can automatically release to Amazon S3 and GitHub Releases. It improves the UX of self-signed apps on Windows, adds a new conveyor run command and has many usability upgrades for developers. Let’s dive in!

File associations

Files and folders are critical tools for organizing work and creating complex results. Desktop apps can easily work with files, but the way you associate apps with file types varies across platforms. Conveyor now abstracts you from the details whilst letting you specify OS-specific metadata if you want precise control.

Adding an association for *.myapp files is as easy as this:

app {
    file-associations = [ .myapp ]
}

Linux requires you to specify MIME types for file associations. Conveyor takes care of that for you by generating a MIME type based on your app’s rdns-name, specifically application/vnd.${app.rdns-name}.${extension}. If you prefer to set the MIME yourself, you can do so like this:

app.file-associations = [ .foo application/x-my-mime-type ]

Some file types may have more than one valid extension, e.g. for an image editor you could write:

app.file-associations += .jpg .jpeg image/jpeg
app.file-associations += .tif .tiff image/tiff

Note here we show how to use the += operator to append to the pre-existing list (which starts out empty).

To handle a common extension like .txt make sure to specify the right MIME type e.g. text/plain.

Apple has a MIME-like system for its own platforms called Uniform Type Identifiers. Conveyor handles all the details for you, but you can specify your own UTIs if you want.

Automatic deployment

The conveyor make copied-site command lets you build and immediately upload the generated set of packages, update metadata site and the download.html page. Previously this feature only supported SFTP upload, now it knows how to upload to AWS and GitHub Releases as well.

Uploading to S3, taking the access key id and secret key from environment variables:

app {  
  site {
    base-url = "https://my-bucket.s3.amazonaws.com/path/to/site"
    s3 {
      region = "us-east-1"
      access-key-id = ${env.AWS_ACCESS_KEY_ID}
      secret-access-key = ${env.AWS_SECRET_ACCESS_KEY}
    }
  }
}

The app.site.copied-to key is automatically inferred to be s3:my-bucket/path/to/site.

Uploading to GitHub Releases, for open source projects:

app {
  # Tell Conveyor the project is open source.
  vcs-url = "github.com/user/repo"
  
  site {    
    github {
      // Token looks like "github_pat_SOME_TOKEN_VALUE"
      oauth-token = ${env.GITHUB_TOKEN}
      
      // Optional: upload the download site to a branch. 
      pages-branch = "gh-pages"
    }
  }
}

Now when executing conveyor make copied-site the generated download.html page will be placed at the root of your GitHub Pages.

With these new capabilities we’ve now achieved our goal of making deploying desktop apps as easy as deploying a web app: generating a fresh app from scratch is one command, and going from the resulting set of compiled app files to a download site filled with self-updating packages is also one command. For bytecode and scripting languages the only remaining difference is signing certificates.

Improved self-signed UX for Windows

Speaking of which, we improved the end-user experience when you’re not using code signing. Users can now simply run the self-signed installer EXE. Your app will trigger security warnings in Edge which the user must click through, but in Chrome there are no such barriers. The installer will install your code signing certificate into the cert store, meaning that from that point on updates to new versions are properly authenticated, Windows has a stable package identity and admins can whitelist your software as they see fit.

Why the change? Previous versions of Conveyor asked Windows users to copy/paste some PowerShell commands to install self-signed certificates, but this wasn’t always ideal. PowerShell comes in multiple versions and the latest version breaks backwards compatibility. Additionally, different versions of Windows require different incantations to ensure scripts can run. Using an installer to add the certificate gets rid of these problems.

Note that you can still use the .appinstaller or .msix files directly, but you’ll need to install the certificate yourself in that case. The raw files are useful for Windows network admins, but our installer EXE wraps the MSIX packaging system with various fixes and improvements.

New run command

When testing you frequently want to build your app into packaged form and then execute it. Previously you had to use the right target for your current host OS, then go into the output directory and run your program from there. It wasn’t hard, but it wasn’t as convenient as possible either.

The new conveyor run command builds the right version of your app for the current OS and then executes it immediately. Note that for speed it doesn’t install the app, it just creates the directory tree that will be installed, signs it and then executes the contents. This lets you quickly test that files are laid out correctly and that your app functions properly when packaged and signed. On macOS there’s no installation process so apps run this way will work in the same way as when copied to /Applications.

File system virtualization control

When your app is run on Windows the kernel will virtualize parts of the file system and registry, redirecting writes to an app private location. In this way uninstallation is guaranteed to be clean so your app can’t accidentally contribute to system “rot”, and there are no tricky permissions issues with app data in other user’s home directories because Windows itself is doing the uninstall.

Virtualization also means any sub-components you may ship that write to shared locations like c:\windows or a shared AppData directory can’t conflict with each other, and ensures that Windows can hard link files at install time from other apps, without any risk of an app modifying them on disk (because any writes trigger copy-on-write redirection).

All very useful! Still, there are times when you may need to opt out:

  • If your app is actually intended to modify the data files of another app. For example, Minecraft modding tools.
  • If it causes bugs.

The March update lets you control this virtualization feature. Here’s a couple of examples:

app.windows.manifests.msix.virtualization {
  # Exclude a single folder within LocalAppData folder from virtualization.
  excluded-directories += LocalAppData/OtherAppsFolder

  # Exclude the entire RoamingAppData folder from virtualization.
  excluded-directories += RoamingAppData
}

Usually you’d only exclude the smallest set of folders you need, but it’s possible to devirtualize the entire AppData directory for both local and roaming data (roaming = copied from the user’s home directory server on login, local = not replicated over the network).

If you’re packaging a JVM app and your conveyor.compatibility-level key is 8 or higher the system-wide temp directory will be devirtualized automatically. This is to avoid a buggy interaction between UNIX domain sockets, the Java 19+ networking stack and Windows.

A couple of other enhancements for Windows development:

  1. Some frameworks or APIs need you to know your “app user model ID” (AUMID). You can now compute this by running conveyor make app-user-model-id.
  2. We’ve refreshed the AppX Manifest schemas bundled with the tool.

What’s that about? One of Conveyor’s core design decisions is to let you control OS specific details as well as providing high level abstractions - you should never have to choose between convenience and control. As a part of that we let you specify fragments of AppX Manifest XML or even the whole file. That lets you get any Windows-specific integration you need. Because the format is so complex we also bundle the XSD validation schemas and run them, ensuring that any mistakes are picked up at package build time rather than install time. Our refresh picks up recently added features and namespaces.

Usability improvements

In every release we review the support tickets filed over that period and make product changes to fix them, like by checking for common mistakes and printing warnings. You’ll see an example of that if you accidentally write config like this:

app {
  app.foo.bar = true
}

That particular copy/paste mistake defines an app object inside the top level app object and is already checked for before the March update. In this release:

  • Conveyor checks that your chosen download site server supports HTTP Range requests. This is required for installation on Windows and resumption of incomplete update downloads on macOS.
  • If you don’t specify the main class of your JVM app then Conveyor discovers it automatically. That is convenient but can be confusing if you think you’re specifying it, got that wrong and then Conveyor picks up an unrelated entry point from a library. We now print a warning when the main class was inferred.

We’ll keep automating the experience gained from support requests over time.

Wrap up

Last but not least we’ve added support for new Electron and JVM versions, and there are a variety of bug fixes. We hope you enjoy the upgrade!

.