corundum-framework-package 0.7.0

A desktop app as a Velopack package: its start and install hooks, told apart from an update
Documentation
# corundum-framework-package

A desktop app as a [Velopack](https://velopack.io) package, for Windows, macOS and Linux.

`start` is the first thing the app's `main` runs. Started by the Windows installer or
uninstaller for a hook, it runs the app's hook and exits; otherwise it tells the app whether
this start is its first, the first after an update, or neither, from the version the app
last ran with. Not in a package (`cargo run`), it says so and does nothing else.

```rust,ignore
use corundum_framework_package::{PackageSpec, Services, Start, start};

static SPEC: PackageSpec = PackageSpec {
    app_name: "my-app",
    on_installed: None,
    on_uninstalling: None,
    before_apply: None,
    cli: None,
    services: Services::NONE,
};

fn main() {
    if let Start::Updated { from, to } = start(&SPEC) {
        println!("updated from {from} to {to}");
    }
}
```

The crate's other parts:

- `Updater` finds and downloads the app's updates on a thread of its own, and applies one
  only when asked, after the app's `before_apply` has stopped its background processes.
  `apply` hands the update to Velopack's updater: with `After::Exit` (a daemon, a command)
  it exits the process; with a restart it returns, and the app ends the process itself, a
  GUI by closing its window. `apply_pending_at_start` applies, at a GUI's start, an update
  downloaded earlier and never applied.
- `cli_path` puts the app's command line on `PATH`: the user `Path` on Windows, from the
  install hook; a link in `/usr/local/bin` on macOS and in `~/.local/bin` on Linux, when
  the user asks. Another program's command of the same name is left alone.
- `console` is the Windows console shim: a GUI binary has no console, so a small console
  binary on `PATH` starts it with the terminal's arguments and streams.

## Background services

`PackageSpec::services` names the app's background services: `Services::NONE`, or with the
`service` feature `Services::new(&[my_service])`, each a function building the service's
`corundum_framework_service::ServiceSpec`. With them:

- `Updater::apply` and `apply_pending_at_start` stop the services that are running after
  the app's `before_apply`, and note them in `services-to-restart` in the app's data
  directory first. A service whose state cannot be read is stopped and noted all the same;
  only a note that cannot be written refuses the update. When the apply fails, they are
  started again at once.
- `start` starts the services an update stopped, on every start, packaged or not, and
  removes the note: whichever of the app's programs starts first after the update does it.
- On Windows, the uninstall hook uninstalls each service before the files go. On macOS and
  Linux nothing runs at uninstall: remove the background service first (Settings › About ›
  Remove), or its unit is left pointing at a program
  that is gone.

## Packaging

The command on `PATH` (`CliSpec::command`) need not be the cargo binary's name. On macOS and
Linux it links to the running executable, whatever `vpk --mainExe` named it; on Windows it
is the shim `<command>.exe` beside the GUI executable in `current\`.

Cargo cannot build two binaries of one name in a workspace (cargo#6313), so an app's desktop
package is built as `<app>-desktop`, while its headless build keeps `<app>`. Pack it so:

- macOS and Linux: rename the desktop binary to `<app>` in the pack directory, and run
  `vpk pack --mainExe <app>`.
- Windows: keep the GUI executable's name, add the console shim as `<app>.exe`, and run
  `vpk pack --mainExe <app>-desktop.exe`. The shim calls `console::forward("<app>-desktop.exe")`.
  Without the shim (`cargo install`, or before packaging) the GUI executable prints in the
  terminal through `corundum_framework_platform::console::attach_parent`, which the shell
  does not wait for: no exit code, no typed input.
- `--packTitle` equals `LauncherSpec::name`, so the installer's shortcut and the launcher's
  path coincide.
- Velopack's `Update.exe` (`UpdateMac` on macOS) stays beside the executable; vpk puts it
  there, and the package's detection looks for it.

On iOS and Android the crate is empty: the store installs and updates the app there.

## License

Licensed under either of the Apache License, Version 2.0 or the MIT license, at your option.