briefcase icon indicating copy to clipboard operation
briefcase copied to clipboard

Custom install / uninstall scripts and options

Open mhsmith opened this issue 9 months ago • 6 comments

We're currently discussing using Briefcase as a backend for the conda constructor tool. To do this would require several new features, but the following two are closely connected:

  • Custom scripts to run during installation and uninstallation.
  • Boolean options that the user can choose during installation and uninstallation.
    • It must also be possible to set these on the command line for a non-interactive install / uninstall, e.g. in an organizational device management system.

The following is a sketch of how Briefcase could support this in a generic way. Like most of the existing pyproject.toml settings, these could be set either at the app level, or per platform.

We're only considering the 3 desktop platforms at the moment. And in the case of macOS, only the .pkg format would support these features.


Supporting the scripts themselves would be the simpler part: add settings to select their paths in the source directory. There would be 4 types of scripts: preinstall, postinstall, preuninstall and postuninstall. "Pre" and "post" here refer to the creation or deletion of the installed directory, so only postinstall and preuninstall would be run with that directory present.

Since the scripts would require an interpreter on the target machine, their supported languages would inevitably be platform-specific. We probably don't need to code specific support for each language, as the scripts would be launched via the standard OS subprocess mechanism. However, we can document that .bat and .ps1 are guaranteed to be supported on Windows, and .sh on Linux and macOS.

Other languages could also be used if the developer knows the target machine will have an interpreter, or if they have bundled an interpreter with the app itself. Depending on how we resolve https://github.com/beeware/briefcase-macOS-app-template/issues/7, we could even use the app's own bundled copy of Python.

The scripts would not be able to take interactive input: they could only print to stdout and stderr, and report success or failure with an exit code.


The boolean options would be specified as a list of items, each of which has the following properties:

  • ID (string)
  • Title (string)
  • Description (string)
  • Command-line flag (string)
  • Default (boolean)
  • Enabled (string: see below)

All of these are self-explanatory except for "enabled", as this may depend on the value of other options, or details of the installation environment. We can therefore express it as a string containing a boolean expression in a simple language supporting only the following elements:

  • Operators and, or and not
  • Parentheses
  • IDs of other options, or IDs pre-defined by Briefcase.

At installer build time, Briefcase would parse this expression with the Python ast module, and transform it into whatever code is necessary to implement it in each installer format.

Currently the only pre-defined ID we expect to need is an indication of whether an installation is in admin mode. For example, an option which should only be enabled in single-user mode would have enabled = "not admin_mode".

At install / uninstall time, the user's chosen options are passed to the scripts using environment variables with the name BRIEFCASE_ followed by the option's ID, and the value 0 or 1. This is likely more convenient than parsing a command line in a .sh or .bat script.


Related:

  • #1014

mhsmith avatar Mar 12 '25 22:03 mhsmith

This all broadly make sense; a couple of questions/clarifications.

.sh and .bat both make sense as "platform native" scripting formats; are you suggesting that we'd explicitly encode support for those extensions, or just rely on system-provided "can execute this in a shell" logic? (i.e., are we invoking /bin/sh, or are we using os.system() (or similar)?

I presume you're considering a TOML table/map as the syntax here? Something like:

[[tool.briefcase.myapp.installer_options]]
id = 'invert'
title = 'Invert Icon'
description = "Use upside down icon for installed app"
flag = "--invert"
default = false

or

[tool.briefcase.myapp.installer_option.invert]
title = 'Invert Icon'
description = "Use upside down icon for installed app"
flag = "--invert"
default = false

To that end - is there any reason that we need to have an explicit flag setting? Could we not infer an option from the ID? (so an ID of invert means a flag of --invert or -X invert or similar)

freakboy3742 avatar Mar 13 '25 01:03 freakboy3742

are you suggesting that we'd explicitly encode support for those extensions, or just rely on system-provided "can execute this in a shell" logic?

The system-provided launcher should be sufficient – I've edited my comment to clarify this.

I presume you're considering a TOML table/map as the syntax here?

Yes, and since order is significant, it would have to be an array.

is there any reason that we need to have an explicit flag setting? Could we not infer an option from the ID?

Yes, the ID could be used as the default flag, and this is probably sufficient for the .msi and .pkg formats.

For the .sh format, there might be a need for single-character flags, and for flags indicating both true and false values – see the source code here. However, it looks like we won't be including .sh in the first phase, so we can probably omit this feature for now.

mhsmith avatar Mar 13 '25 11:03 mhsmith

This might be useful:

I require this in my installer for post-install and post-uninstall (but without user interactions). For windows, I've implemented this using a simple custom action in the wix file (.wxs). I've ended up modifying the template and saving it and just pointing briefcase to it in the toml file.

You can find information on how to add custom actions here: https://www.advancedinstaller.com/versus/wix-toolset/add-custom-actions.html

Bonus: you can also show a customized message in the installer while the script in running.

MosGeo avatar Mar 15 '25 17:03 MosGeo

For windows, I've implemented this using a simple custom action in the wix file (.wxs). I've ended up modifying the template and saving it and just pointing briefcase to it in the toml file.

@mhsmith How did you do this? I've searched Briefcase documentation and found no information about specifying a custom .wxs file.

fedelibre avatar Aug 30 '25 17:08 fedelibre

Since the .wxs file is in the template, it can be customized by editing the template:

  • Clone https://github.com/beeware/briefcase-windows-app-template/
  • Edit your local copy.
  • Add a template setting to your pyproject.toml file.
  • Rerun briefcase create.

mhsmith avatar Aug 31 '25 10:08 mhsmith

For windows, I've implemented this using a simple custom action in the wix file (.wxs). I've ended up modifying the template and saving it and just pointing briefcase to it in the toml file.

@MosGeo: is your custom template online anywhere?

mhsmith avatar Oct 05 '25 14:10 mhsmith