The bundler is responsible for creating a single-file distributable called “TinyPilot bundle”.
The TinyPilot bundle contains all the TinyPilot-owned code required to install TinyPilot on a device.
The create-bundle
script generates the bundle from the bundle/
folder. That folder contains a few “static” configuration files. At build time, the create-bundle
script adds the following dependencies:
- The TinyPilot web service
- The root
Dockerfile
packs the TinyPilot web service as a Debian package from the source files.
- The root
- Metadata
- E.g., version/build information
The entrypoint for installing the bundle is the bundle/install
script. It contains the installation logic that installs TinyPilot and all of its dependencies.
Gatekeeper is TinyPilot’s web service for hosting and distributing bundles. See the Gatekeeper README for more info.
Our CircleCI pipeline automatically builds and uploads new bundles to Gatekeeper:
- For Community, it releases every commit to the
master
branch - For Pro, it only releases tags with a particular format (e.g.,
2.4.1
)
get-tinypilot.sh
(get-tinypilot-pro.sh
for Pro) facilitates the installation process.
For installing TinyPilot on the device, get-tinypilot.sh
unpacks the bundle to /mnt/tinypilot-installer
and invokes the install
script.
To avoid excessive writes to the filesystem, the bundle is downloaded and unpacked on a volatile RAMdisk mounted at /mnt/tinypilot-installer
.
On a fresh device, the user runs get-tinypilot.sh
manually. On a device with an existing TinyPilot installation, TinyPilot’s update process invokes get-tinypilot.sh
“under the hood”.
get-tinypilot.sh
is idempotent, so it’s safe to run repeatedly.
The installation procedure consists of the following steps. The procedure is slightly different between TinyPilot Community and Pro.
get-tinypilot.sh
retrieves latest bundle from Gatekeeper.get-tinypilot.sh
unpacks bundle and invokesinstall
script.
get-tinypilot-pro.sh
checks whether the caller supplied a version flag.- If the version flag is absent,
get-tinypilot-pro.sh
asks Gatekeeper what the latest available version is.
- If the version flag is absent,
get-tinypilot-pro.sh
requests the bundle with the desired version from Gatekeeper.get-tinypilot-pro.sh
unpacks bundle and invokesinstall
script.
When performing a version-to-version update, TinyPilot carries out the above installation procedure automatically after the user requests an update via the web UI.
- From the TinyPilot web UI, the user clicks “System” > “Update”.
- TinyPilot’s web service backend returns two versions:
- The one that is currently installed on the device.
- The latest available version, which the TinyPilot web service retrieves from Gatekeeper.
- If the versions are different, the frontend shows an “Update” button.
- When the user clicks on “Update”, TinyPilot’s web service backend runs the update launcher asynchronously.
- On TinyPilot Pro, the update launcher stores the desired target version in a file.
- The update launcher starts the
tinypilot-updater
systemd service. - The
tinypilot-updater
systemd service executes theupdate-service
Python script. - The
update-service
invokes the privileged/opt/tinypilot-privileged/scripts/update
script. - The privileged
update
script downloadsget-tinypilot.sh
/get-tinypilot-pro.sh
(see above) and executes that script in the privileged context.- On TinyPilot Pro, the privileged
update
script reads and passes on the target version from the file that the update launcher had populated previously.
- On TinyPilot Pro, the privileged
The indirection in the update flow has the following reasons:
- The system update is a long-running process, so we use systemd to run it asynchronously.
- To avoid an elevation of privilege vulnerability, the
update-service
(owned by thetinypilot
user) delegates to the privilegedupdate
script (owned by theroot
user).
There is one main difference between the Community and Pro edition:
- On TinyPilot Community, the update process always installs the latest available version (i.e., built from the latest commit to the
master
branch). - On TinyPilot Pro, the update process installs the particular version that was returned from Gatekeeper and shown in the UI.
Until August/September 2022, the installation and update flows used to look differently. You can read up on the rationale behind the overhaul in detail in the “update overhaul document”. We broke down the overhaul into individual tasks, which you can find in this mega ticket.
From a high level, the differences were:
- The previous update flow relied on git repositories. So instead of shipping a self-contained bundle, the deployment of new code happened via
git pull
. However,git
is not the right tool for software distribution, so over time we ran into several issues and limitations with this setup. - There were no license checks for TinyPilot Pro. Instead, the only protection mechanism of the Pro source code was by keeping the URL of the git mirror secret.
- Because the legacy update flow depended on commodity Git hosting, it prevented us from controlling important parts of the update experience such as ensuring valid version transitions, phasing rollouts into stages, and performing license checks.