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
138 changes: 132 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,9 @@ jobs:

# Linux is a shipped target from this change on. Same reasoning as
# build-windows: `cargo test --lib` proves the crate compiles, not that an
# AppImage and a deb come out. The name assertions match the exact filenames
# release.yml's verification step requires, so a bundler naming change fails
# here rather than after a tag is pushed.
# AppImage, a deb and an rpm come out. The name assertions match the exact
# filenames release.yml's verification step requires, so a bundler naming
# change fails here rather than after a tag is pushed.
build-linux:
name: build-linux
# 24.04, not 22.04: the ONNX Runtime binary fastembed downloads is built
Expand Down Expand Up @@ -366,8 +366,24 @@ jobs:
- name: Install deps
run: npm ci

# Tauri fetches its own linuxdeploy, a July 2024 build, into ~/.cache/tauri
# and reuses whatever is there. That build still copies Ubuntu's
# libwayland-client into the AppImage, and Fedora's and Arch's Mesa need
# a symbol from a newer one, so the WebKit web process aborted with
# "Could not create default EGL display" and the window stayed white
# (#130). linuxdeploy excludes the library since November 2024; seed the
# cache with a release that has it, pinned by checksum.
- name: Use a linuxdeploy that leaves libwayland-client to the host
run: |
set -euo pipefail
mkdir -p ~/.cache/tauri
curl -fsSL -o ~/.cache/tauri/linuxdeploy-x86_64.AppImage \
https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20251107-1/linuxdeploy-x86_64.AppImage
echo "c20cd71e3a4e3b80c3483cef793cda3f4e990aca14014d23c544ca3ce1270b4d $HOME/.cache/tauri/linuxdeploy-x86_64.AppImage" | sha256sum -c
chmod +x ~/.cache/tauri/linuxdeploy-x86_64.AppImage

- name: Build app and bundles
run: npx tauri build --config src-tauri/tauri.ci.conf.json --bundles appimage deb
run: npx tauri build --config src-tauri/tauri.ci.conf.json --bundles appimage deb rpm

- name: Assert bundles exist with the names the release expects
run: |
Expand All @@ -376,7 +392,117 @@ jobs:
version=$(node -p "require('./package.json').version")
appimage="$root/appimage/Moldavite_${version}_amd64.AppImage"
deb="$root/deb/Moldavite_${version}_amd64.deb"
ls -la "$root/appimage" "$root/deb" || true
rpm="$root/rpm/Moldavite-${version}-1.x86_64.rpm"
ls -la "$root/appimage" "$root/deb" "$root/rpm" || true
[ -f "$appimage" ] || { echo "::error::Expected $appimage"; exit 1; }
[ -f "$deb" ] || { echo "::error::Expected $deb"; exit 1; }
du -h "$appimage" "$deb"
[ -f "$rpm" ] || { echo "::error::Expected $rpm"; exit 1; }
du -h "$appimage" "$deb" "$rpm"
# The host's Mesa resolves against libwayland-client; a bundled copy
# is the #130 blank window on Fedora and Arch.
extracted=$(mktemp -d)
(cd "$extracted" && "$GITHUB_WORKSPACE/$appimage" --appimage-extract >/dev/null)
if find "$extracted/squashfs-root" -name 'libwayland-client.so*' | grep -q .; then
echo "::error::The AppImage bundles libwayland-client (#130)"; exit 1
fi

- name: Keep the bundles for the Fedora smoke test
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: linux-bundles
path: |
src-tauri/target/release/bundle/appimage/Moldavite_*_amd64.AppImage
src-tauri/target/release/bundle/rpm/Moldavite-*.x86_64.rpm
if-no-files-found: error
retention-days: 3

# The AppImage carries Ubuntu's GTK and WebKitGTK and borrows the graphics
# stack from whatever distribution runs it. Fedora's Mesa is newer than
# Ubuntu 24.04's, and that mix is what shipped a blank white window in 2.6.0
# (#130): the WebKit web process aborted in EGL setup before the first
# paint, and nothing in CI could have noticed because the only Linux that
# ever ran the bundles was the Ubuntu that built them. The rpm uses Fedora's
# own WebKitGTK instead, and resolving its dependencies is part of the test.
# Each bundle has to launch under Xvfb on Fedora, keep its web process alive,
# and paint something other than a blank window.
smoke-linux-fedora:
name: smoke-linux-fedora
needs: build-linux
runs-on: ubuntu-24.04
container:
image: fedora:44
timeout-minutes: 20
steps:
- name: Download the Linux bundles
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: linux-bundles
path: bundles

- name: Install a headless desktop and the rpm
run: |
set -euo pipefail
dnf install -y --setopt=install_weak_deps=False \
xorg-x11-server-Xvfb ImageMagick mesa-dri-drivers mesa-libEGL mesa-libGL \
dbus-daemon dbus-tools xdg-utils desktop-file-utils procps-ng
rpm=$(find bundles -name 'Moldavite-*.x86_64.rpm' | head -n 1)
dnf install -y "$rpm"
# Extraction stands in for FUSE, which the container lacks. AppRun is
# what the AppImage runtime executes after mounting the same tree.
appimage=$(find bundles -name 'Moldavite_*_amd64.AppImage' | head -n 1)
chmod +x "$appimage"
(cd bundles && "$GITHUB_WORKSPACE/$appimage" --appimage-extract >/dev/null)

- name: Launch each bundle under Xvfb
run: |
set -euo pipefail
smoke() {
local label=$1; shift
local log="$RUNNER_TEMP/$label.log" shot="$RUNNER_TEMP/$label.png"
Xvfb :99 -screen 0 1280x800x24 -nolisten tcp >/dev/null 2>&1 &
local xvfb=$!
sleep 2
# setsid: the app, its WebKit processes and the session bus form
# one process group that can be torn down together afterwards.
DISPLAY=:99 setsid dbus-run-session -- "$@" >"$log" 2>&1 &
local app=$!
sleep 30
local alive=1 web colors
kill -0 "$app" 2>/dev/null || alive=0
web=$(pgrep -c -f WebKitWebProcess || true)
magick import -display :99 -window root "$shot" || true
colors=$(magick identify -format '%k' "$shot" 2>/dev/null || echo 0)
kill -TERM -- -"$app" 2>/dev/null || true
sleep 2
kill -KILL -- -"$app" 2>/dev/null || true
pkill -KILL -f WebKitWebProcess || true
kill "$xvfb" 2>/dev/null || true
wait "$xvfb" 2>/dev/null || true
echo "== $label: alive=$alive web-processes=$web distinct-colors=$colors"
cat "$log"
local failed=0
if grep -q 'Could not create default EGL display' "$log"; then
echo "::error::$label aborted in EGL setup, the #130 blank window"; failed=1
fi
[ "$alive" = 1 ] || { echo "::error::$label exited during startup"; failed=1; }
[ "$web" -gt 0 ] || { echo "::error::$label has no WebKit web process"; failed=1; }
# A mapped but unpainted window is two colors: its white and the
# black root. Any rendered text pushes this into the hundreds.
[ "$colors" -gt 32 ] || { echo "::error::$label painted a blank window ($colors colors)"; failed=1; }
return $failed
}
status=0
smoke appimage "$GITHUB_WORKSPACE/bundles/squashfs-root/AppRun" || status=1
smoke rpm /usr/bin/moldavite || status=1
exit $status

- name: Keep the screenshots and logs
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: linux-smoke
path: |
${{ runner.temp }}/*.png
${{ runner.temp }}/*.log
if-no-files-found: warn
retention-days: 3
37 changes: 31 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,10 @@ jobs:
`3. Windows may show SmartScreen warning - click "More info" → "Run anyway"`,
``,
`### Linux (beta)`,
`1. Download \`Moldavite_${process.env.PACKAGE_VERSION}_amd64.AppImage\` (any distribution) or \`Moldavite_${process.env.PACKAGE_VERSION}_amd64.deb\` (Debian, Ubuntu)`,
`1. Download \`Moldavite_${process.env.PACKAGE_VERSION}_amd64.AppImage\` (any distribution), \`Moldavite_${process.env.PACKAGE_VERSION}_amd64.deb\` (Debian, Ubuntu) or \`Moldavite-${process.env.PACKAGE_VERSION}-1.x86_64.rpm\` (Fedora)`,
`2. AppImage: mark it executable and run it. Needs glibc 2.38 or newer (Ubuntu 24.04, Debian 13, Fedora 39 or later) and \`libfuse2\` (\`sudo apt install libfuse2\` on Ubuntu)`,
`3. deb: \`sudo apt install ./Moldavite_${process.env.PACKAGE_VERSION}_amd64.deb\``,
`4. rpm: \`sudo dnf install ./Moldavite-${process.env.PACKAGE_VERSION}-1.x86_64.rpm\``,
].join('\n');
const { data } = await github.rest.repos.createRelease({
owner: context.repo.owner,
Expand Down Expand Up @@ -107,7 +108,7 @@ jobs:
# 24.04: the ONNX Runtime binary fastembed downloads needs glibc 2.38,
# which 22.04 lacks. The bundles therefore need 2.38 at runtime too.
- platform: 'ubuntu-24.04'
args: '--bundles appimage deb'
args: '--bundles appimage deb rpm'

runs-on: ${{ matrix.platform }}
steps:
Expand Down Expand Up @@ -243,8 +244,25 @@ jobs:
patchelf

# No code signing on Linux; the AppImage is updater-signed like every
# other platform. The deb has no in-app updater and is replaced by
# installing the next one.
# other platform. The deb and the rpm have no in-app updater and are
# replaced by installing the next one.
# Tauri fetches its own linuxdeploy, a July 2024 build, into ~/.cache/tauri
# and reuses whatever is there. That build still copies Ubuntu's
# libwayland-client into the AppImage, and Fedora's and Arch's Mesa need
# a symbol from a newer one, so the WebKit web process aborted with
# "Could not create default EGL display" and the window stayed white
# (#130). linuxdeploy excludes the library since November 2024; seed the
# cache with a release that has it, pinned by checksum.
- name: Use a linuxdeploy that leaves libwayland-client to the host
if: matrix.platform == 'ubuntu-24.04'
run: |
set -euo pipefail
mkdir -p ~/.cache/tauri
curl -fsSL -o ~/.cache/tauri/linuxdeploy-x86_64.AppImage \
https://github.com/linuxdeploy/linuxdeploy/releases/download/1-alpha-20251107-1/linuxdeploy-x86_64.AppImage
echo "c20cd71e3a4e3b80c3483cef793cda3f4e990aca14014d23c544ca3ce1270b4d $HOME/.cache/tauri/linuxdeploy-x86_64.AppImage" | sha256sum -c
chmod +x ~/.cache/tauri/linuxdeploy-x86_64.AppImage

- name: Build Linux app
if: matrix.platform == 'ubuntu-24.04'
uses: tauri-apps/tauri-action@84b9d35b5fc46c1e45415bdb6144030364f7ebc5 # v0
Expand Down Expand Up @@ -436,6 +454,8 @@ jobs:
`Moldavite_${version}_amd64.AppImage.sig`,
`Moldavite_${version}_amd64.deb`,
`Moldavite_${version}_amd64.deb.sig`,
`Moldavite-${version}-1.x86_64.rpm`,
`Moldavite-${version}-1.x86_64.rpm.sig`,
`latest.json`,
// The Chrome/Edge/Brave clipper, loaded unpacked. The Firefox XPI
// is not here: it has to be signed through AMO by hand, and is
Expand Down Expand Up @@ -518,11 +538,16 @@ jobs:
asset: `Moldavite_${version}_amd64.AppImage`,
signature: `Moldavite_${version}_amd64.AppImage.sig`
},
// The updater never installs a deb, but tauri-action signs it and
// lists it, so verify the signature it advertises like the rest.
// The updater never installs a deb or an rpm, but tauri-action
// signs and lists both, so verify the signatures it advertises
// like the rest.
'linux-x86_64-deb': {
asset: `Moldavite_${version}_amd64.deb`,
signature: `Moldavite_${version}_amd64.deb.sig`
},
'linux-x86_64-rpm': {
asset: `Moldavite-${version}-1.x86_64.rpm`,
signature: `Moldavite-${version}-1.x86_64.rpm.sig`
}
};

Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ All notable changes to Moldavite are documented here.

### Added

- **An rpm for Fedora.** Linux releases now ship `Moldavite-x.x.x-1.x86_64.rpm` next to the AppImage and the deb, installed with `sudo dnf install ./Moldavite-x.x.x-1.x86_64.rpm` and updated by installing the next one.

- **Start your synced Forge on Mac.** Turn on Use synced Forge in Settings → General to create a separate iCloud folder, write notes, or open it in Finder. Connect the same Forge on iPhone or iPad later using the same iCloud account. Your existing local Forges stay separate.

- A reusable Moldavite brand kit and branded iPhone, iPad and App Store icons.
Expand All @@ -18,6 +20,8 @@ All notable changes to Moldavite are documented here.

### Fixed

- **The Linux AppImage opened a blank white window on Fedora and Arch.** It carried Ubuntu's Wayland client library, and the newer Mesa on those distributions needs a symbol it lacks, so the WebKit web process aborted with "Could not create default EGL display: EGL_BAD_PARAMETER" before the first paint. The AppImage now leaves that library to the system, and every pull request launches the AppImage and the rpm on Fedora to prove they paint. (#130)

- An unpinned MCP client now reports that iCloud is unsupported when the synced Forge is active, instead of silently selecting the previous local Forge. Pin MCP clients to a local Forge with `--forge`.

- iOS native controls and status-bar contrast follow the app theme. Long dialog
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ npm run format:check # Prettier check
(cd src-tauri && cargo clippy --all-targets -- -D warnings)
npm run build # Frontend production build
npm run tauri build # Packaged app (DMG on macOS, EXE and MSI on Windows;
# add --bundles appimage deb on Linux)
# add --bundles appimage deb rpm on Linux)
```

Run the checks relevant to your change while iterating. Before requesting review,
Expand Down
13 changes: 7 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,13 @@ choose **More info → Run anyway**. Updates delivered inside the app are
cryptographically signed and verified before they install.

**Linux (beta)** — download `Moldavite_x.x.x_amd64.AppImage` for any
distribution, or `Moldavite_x.x.x_amd64.deb` for Debian and Ubuntu. Both need
glibc 2.38 or newer, which means Ubuntu 24.04, Debian 13, Fedora 39 or later;
the local semantic-search runtime sets that floor. Mark the AppImage executable
once, and install `libfuse2` if your distribution does not ship it
(`sudo apt install libfuse2` on Ubuntu). In-app updates work for the AppImage.
The deb is updated by installing the next one.
distribution, `Moldavite_x.x.x_amd64.deb` for Debian and Ubuntu, or
`Moldavite-x.x.x-1.x86_64.rpm` for Fedora. All need glibc 2.38 or newer, which
means Ubuntu 24.04, Debian 13, Fedora 39 or later; the local semantic-search
runtime sets that floor. Mark the AppImage executable once, and install
`libfuse2` if your distribution does not ship it (`sudo apt install libfuse2`
on Ubuntu). In-app updates work for the AppImage. The deb and the rpm are
updated by installing the next one.

## Connect your AI

Expand Down
2 changes: 1 addition & 1 deletion docs/PROJECT_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
This does not change the app's unreleased iOS status.

- Windows is a beta release target. Every PR runs clippy and the Rust library test suite on `windows-latest`; no Windows runtime journey has been exercised manually, so Windows coverage is CI-backed and the platform stays beta until it is not
- Linux is a beta release target: an AppImage (any distribution, carries the updater) and a deb (Debian and Ubuntu, no in-app updater), built by the release workflow and proven by a `build-linux` job on every PR that asserts the exact artifact names the release verification requires. The crate has compiled and tested on `ubuntu-latest` since CI existed. No Linux runtime journey has been exercised by hand. Both bundles need glibc 2.38 or newer (Ubuntu 24.04, Debian 13, Fedora 39 or later) because the ONNX Runtime binary fastembed ships is built against it; the deb declares `libc6 (>= 2.38)` so apt refuses cleanly on older systems, and the AppImage needs `libfuse2` where the distribution does not ship it
- Linux is a beta release target: an AppImage (any distribution, carries the updater), a deb (Debian and Ubuntu) and an rpm (Fedora), the last two without an in-app updater, built by the release workflow and proven by a `build-linux` job on every PR that asserts the exact artifact names the release verification requires, then a `smoke-linux-fedora` job that launches the AppImage and the rpm under Xvfb in a Fedora container and fails on an EGL abort, a dead web process or a blank window (the 2.6.0 AppImage shipped exactly that on Fedora, #130). The crate has compiled and tested on `ubuntu-latest` since CI existed. No Linux runtime journey has been exercised by hand. All bundles need glibc 2.38 or newer (Ubuntu 24.04, Debian 13, Fedora 39 or later) because the ONNX Runtime binary fastembed ships is built against it; the deb declares `libc6 (>= 2.38)` so apt refuses cleanly on older systems, and the AppImage needs `libfuse2` where the distribution does not ship it
- Calendar in right panel + timeline, read-only, from two sources: Apple (EventKit, permission-gated, macOS only) and Google (Calendar API v3 over PKCE loopback OAuth, all platforms, refresh token in the OS credential store). Per-source failures are reported without blanking the other source; Google needs `MOLDAVITE_GOOGLE_CLIENT_ID`/`_SECRET` at build time or it reports unavailable
- macOS builds are signed and notarized. Windows installers are unsigned and may trigger SmartScreen because they are not Authenticode-signed. Linux bundles are unsigned as well; Linux has no equivalent warning. Updater artifacts are signed for every platform, including Windows, and clients verify them before installation. Checks run about 15 seconds after launch, every 24 hours while open, and on focus after 24 hours without a successful check, and can be switched off in Settings → About (manual checks still work); automatic network/404 failures stay silent and retry, while pending versions add accent dots to Settings and About plus the existing install action. Manual checks retain explicit errors, and completed upgrades show the CHANGELOG-backed "What's New" popup (see docs/RELEASING.md)
- Themes/presets, platform-specific keyboard shortcut labels and overlay (⌘? on macOS, Ctrl+? on Windows and Linux), settings modal with focus trap
Expand Down
Loading
Loading