Files
dikte/packaging/build-appimage.sh
yusufipek 7e510f8b35 Build an AppImage and a disk image, and publish them
Installing meant cloning the repository and running a shell script, which
is a fair ask of somebody who already has a terminal open and no ask at
all of anybody else. The releases page now carries an AppImage and a disk
image per Mac architecture: a push to master rebuilds a rolling "latest",
a v* tag publishes a version and leaves it there, and the Run button in
the Actions tab raises the number by running scripts/release.sh, which is
the same script and not a second copy of what it does.

Two things in the application had to give for that. A build has no
__main__.py on disk, and an AppImage is mounted somewhere new every run,
so the command a shortcut is registered with cannot go on being this
interpreter and this file; ipc.launcher() answers with the AppImage or
the bundle instead. And a build carries its own libstdc++, which every
process it starts inherits through LD_LIBRARY_PATH and none of them can
live with: ffmpeg, ydotool and wl-copy are the distribution's binaries
built against the distribution's libraries, and AppImageLauncher, which
is what starting the AppImage again goes through, refuses outright.
integrate.py puts that variable back before anything else runs.

Nothing installs itself over an installation that is already there.
install.sh's menu entry, install-mac.sh's login item and the desktop file
AppImageLauncher writes are each recognised and left alone, so trying a
download once does not quietly move the machine onto it. `dikte
integrate` is how you ask for it outright, and --remove takes it back.

The disk image carries an ffmpeg, pinned and checksummed, because macOS
records through one and ships nothing like it. It is signed ad-hoc and
not with an Apple certificate, so a first launch is refused until Open
Anyway and the permissions are asked for again after each update; both
READMEs and the release notes say so.
2026-08-16 15:03:44 +03:00

78 lines
3.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# The Linux download: one file, no dependencies of its own beyond the sound
# and clipboard programs that come with the desktop.
#
# Run from anywhere; it works in build/ at the top of the checkout and leaves
# the finished AppImage in dist/. The release workflow runs it on the oldest
# Ubuntu still supported, because the glibc a build is linked against is the
# oldest one it will run on, and nothing here depends on which Ubuntu that is.
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
BUILD="$ROOT/build"
APPDIR="$BUILD/AppDir"
OUT="$ROOT/dist"
ARCH="${ARCH:-$(uname -m)}"
export ARCH
VERSION="$(cd "$ROOT" && python3 -c 'import dikte; print(dikte.__version__)')"
rm -rf "$BUILD" "$OUT"
mkdir -p "$APPDIR/usr/bin" "$OUT"
# 1. The application -------------------------------------------------------
python3 -m PyInstaller "$ROOT/packaging/dikte.spec" \
--distpath "$BUILD/dist" --workpath "$BUILD/work" --noconfirm --clean
cp -a "$BUILD/dist/dikte/." "$APPDIR/usr/bin/"
# 2. The icon --------------------------------------------------------------
# Drawn by the application itself, which is why there is no image file in the
# repository and no second place to change what Dikte looks like. Offscreen,
# since this runs with no display anywhere near it.
icons="$BUILD/icons"
QT_QPA_PLATFORM=offscreen PYTHONPATH="$ROOT" \
python3 -m dikte.trayicon --hicolor "$icons"
mkdir -p "$APPDIR/usr/share/icons"
cp -a "$icons/hicolor" "$APPDIR/usr/share/icons/"
# At the top as well, under the name the desktop entry gives: that copy is what
# appimagetool reads, and what a desktop shows before the file is ever run.
cp "$icons/hicolor/256x256/apps/dikte.png" "$APPDIR/dikte.png"
# 3. What the runtime reads ------------------------------------------------
# AppRun is started from the mount point, which is a different path every run,
# so it has to find its own directory rather than be told one.
cat > "$APPDIR/AppRun" <<'EOF'
#!/bin/sh
HERE="$(dirname "$(readlink -f "$0")")"
exec "$HERE/usr/bin/dikte" "$@"
EOF
chmod +x "$APPDIR/AppRun"
# Exec names the file rather than a path: a desktop that integrates the
# AppImage rewrites this line with wherever the user keeps it, and Dikte writes
# its own copy of this entry on first run, which is the one that matters.
cat > "$APPDIR/dikte.desktop" <<EOF
[Desktop Entry]
Type=Application
Name=Dikte
Comment=Voice dictation: record, transcribe, clean up, paste
Exec=dikte
Icon=dikte
Categories=Utility;AudioVideo;
Terminal=false
StartupNotify=false
EOF
# 4. The AppImage ----------------------------------------------------------
# appimagetool is itself an AppImage, and a container or a CI runner has no
# FUSE for it to mount itself with, so it is asked to unpack instead.
tool="$BUILD/appimagetool"
if [ ! -x "$tool" ]; then
curl -fsSL -o "$tool" \
"https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-$ARCH.AppImage"
chmod +x "$tool"
fi
"$tool" --appimage-extract-and-run "$APPDIR" "$OUT/Dikte-$VERSION-$ARCH.AppImage"
echo "dist/Dikte-$VERSION-$ARCH.AppImage"