Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions .github/workflows/desktop.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Build the desktop app on every push, and attach it to every release.
#
# On a push, the Windows installer and the Linux .deb are built
# On a push, the Windows installer and the Linux .deb and AppImage are built
# and kept as workflow artifacts, so a change that breaks them shows up on the
# commit that made it, and anyone can try that commit's build.
#
Expand Down Expand Up @@ -244,9 +244,12 @@ jobs:
working-directory: desktop
run: npm ci

- name: Build the .deb
- name: Build the .deb and the AppImage
working-directory: desktop
env:
# linuxdeploy is itself an AppImage, and a runner has no FUSE.
APPIMAGE_EXTRACT_AND_RUN: '1'
NO_STRIP: 'true'
MPFTP_EMU_RUNTIME: ${{ github.workspace }}/.workbench/assets/pydevices
# The cargo cache restores target/, packages from earlier versions included.
run: |
Expand All @@ -271,6 +274,14 @@ jobs:
ls -l "$deb"
sha256sum "$deb"
echo "deb=$deb" >> "$GITHUB_OUTPUT"
appimages=("$bundle"/appimage/*.AppImage)
[[ ${#appimages[@]} -eq 1 ]] || { echo "::error::expected one AppImage, found ${#appimages[@]}"; exit 1; }
ls -l "${appimages[0]}"
sha256sum "${appimages[0]}"
echo "appimage=${appimages[0]}" >> "$GITHUB_OUTPUT"
# One folder, so the artifact holds both files at its top level.
mkdir linux-out
cp "$deb" "${appimages[0]}" linux-out/

# Run what was built, as someone would: install the .deb, start the app
# on a virtual display, and check that it started its Python host and
Expand All @@ -280,10 +291,15 @@ jobs:
DEB: ${{ steps.built.outputs.deb }}
run: bash desktop/scripts/smoke_linux.sh deb "$DEB"

- name: Run the AppImage
env:
APPIMAGE: ${{ steps.built.outputs.appimage }}
run: bash desktop/scripts/smoke_linux.sh appimage "$APPIMAGE"

- uses: actions/upload-artifact@v7
with:
name: mpftp-desktop-linux-x64
path: ${{ steps.built.outputs.deb }}
path: linux-out/
if-no-files-found: error
compression-level: 0

Expand Down Expand Up @@ -322,8 +338,8 @@ jobs:
run: |
set -euo pipefail
shopt -s nullglob
files=(dist/*-setup.exe dist/*.deb)
[[ ${#files[@]} -eq 2 ]] || { echo "::error::expected two installers, found ${#files[@]}"; ls -l dist; exit 1; }
files=(dist/*-setup.exe dist/*.deb dist/*.AppImage)
[[ ${#files[@]} -eq 3 ]] || { echo "::error::expected three installers, found ${#files[@]}"; ls -l dist; exit 1; }
# Installers are named with the semver form: 0.1.5rc2 is 0.1.5-rc.2.
semver="$(sed -E 's/^([0-9]+\.[0-9]+\.[0-9]+)(a|b|rc)([0-9]+)$/\1-\2.\3/; s/^([0-9]+\.[0-9]+\.[0-9]+)\.dev([0-9]+)$/\1-dev.\2/' <<< "$version")"
for f in "${files[@]}"; do
Expand Down
2 changes: 0 additions & 2 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ if something is broken or missing for you, open an
- **One-click installers.** mpftp with its own Python inside. [Desktop](desktop/README.md)
is a preview for Windows and Linux, attached to every release; next come
signed installers and macOS.
- **Desktop as an AppImage.** One Linux download that carries its own
libraries, for distributions the `.deb` doesn't cover.
- **Desktop updates itself.** Today it links to the latest release; an
updater would fetch and install a new version for you.
- **Where a search result comes from, in the row.** Package search in the
Expand Down
8 changes: 6 additions & 2 deletions desktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ installers next to the wheel and the VS Code extension:
`sudo apt install ./mpftp_<version>_amd64.deb` and start `mpftp`. With
arguments, such as `mpftp --help` or `mpftp ls -d /dev/ttyACM0`, the same
command is the CLI, run with the Python the package carries.
- **Other Linux distributions:** `mpftp_<version>_amd64.AppImage`, one file
that carries its own libraries. Make it executable (`chmod +x`) and run it;
with arguments it is the CLI, as with the `.deb`. If your system has no FUSE,
run it with `--appimage-extract-and-run`.

To serial ports on Linux, your user needs to be in the `dialout` group (or
`uucp` on some distributions).
Expand Down Expand Up @@ -106,10 +110,10 @@ Then, from `desktop/`:
```bash
npm ci
npm run stage:linux # a python-build-standalone Python with mpftp, in python-linux/
npm run build:linux # the .deb
npm run build:linux # the .deb and the AppImage
```

It lands in `src-tauri/target/release/bundle/deb/`.
They land in `src-tauri/target/release/bundle/deb/` and `.../appimage/`.
`tauri.linux.conf.json` swaps the Windows Python for the Linux one, and
[`scripts/smoke_linux.sh`](scripts/smoke_linux.sh) starts a built package and checks that its Python
host comes up.
4 changes: 3 additions & 1 deletion desktop/scripts/smoke_linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ for name in host sidecar; do
|| { echo "::error::the $name runs $exe, not the bundled $python"; fail=1; }
done

app_pid="$(pgrep -f -x "$app" || pgrep -n -f 'squashfs-root/usr/bin/mpftp|/usr/bin/mpftp' || true)"
# The app is the host's parent. An AppImage's AppRun starts it as a bare
# `mpftp`, so its command line doesn't name the path.
app_pid="$(ps -o ppid= -p "${host%%$'\n'*}" 2>/dev/null | tr -d ' ' || true)"
echo "app: ${app_pid:-not found}"
[[ -n "$app_pid" ]] && kill ${app_pid}
for _ in $(seq 15); do
Expand Down
6 changes: 6 additions & 0 deletions desktop/src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,9 @@ fn spawn_host(python: &Path, log: &Path) -> std::io::Result<(Child, ChildStdin,
cmd.args(["-m", "mpftp.desktop_host"])
// The host starts its sidecar with this same Python.
.env("MPFTP_PYTHON", python)
// An AppImage's AppRun points these at its own usr/, which the bundled Python must not see.
.env_remove("PYTHONHOME")
.env_remove("PYTHONPATH")
.env("PYTHONNOUSERSITE", "1")
.env("PYTHONUNBUFFERED", "1")
.env("PYTHONUTF8", "1")
Expand Down Expand Up @@ -615,6 +618,9 @@ fn run_cli_if_asked() {
.args(&args)
// The CLI starts its sidecar with this same Python.
.env("MPFTP_PYTHON", &python)
// An AppImage's AppRun points these at its own usr/, which the bundled Python must not see.
.env_remove("PYTHONHOME")
.env_remove("PYTHONPATH")
.env("PYTHONNOUSERSITE", "1")
.exec();
eprintln!("mpftp: could not run {}: {err}", python.display());
Expand Down
2 changes: 1 addition & 1 deletion desktop/src-tauri/tauri.linux.conf.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"bundle": {
"targets": ["deb"],
"targets": ["deb", "appimage"],
"resources": {
"../python-win/": null,
"../python-linux/": "python/"
Expand Down
Loading