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
129 changes: 128 additions & 1 deletion .github/workflows/flutter-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ jobs:
sudo apt-get update
sudo apt-get install -y \
apparmor-profiles \
at-spi2-core \
bubblewrap \
clang \
cmake \
dbus-daemon \
fonts-noto-core \
fonts-noto-cjk \
fonts-noto-mono \
Expand Down Expand Up @@ -89,6 +91,9 @@ jobs:
- name: Resolve locked dependencies
run: flutter pub get --enforce-lockfile

- name: Test production GTK accent lookup and observer
run: xvfb-run -a env GDK_BACKEND=x11 bash tools/test_gtk_accent.sh

- name: Generate localizations
run: flutter gen-l10n

Expand Down Expand Up @@ -121,6 +126,9 @@ jobs:
- name: Build Linux release
run: flutter build linux --release

- name: Test production GTK accent channel host and lifecycle
run: xvfb-run -a env GDK_BACKEND=x11 bash tools/test_gtk_accent_host.sh

- name: Verify release ships catalog and notices without language pairs
run: |
spelling_root="build/linux/x64/release/bundle/share/busymark/spelling"
Expand Down Expand Up @@ -230,6 +238,11 @@ jobs:
- name: Check out repository
uses: actions/checkout@v7

- name: Prepare clean Snap artifact selection
run: |
rm -f ./*.snap
touch "${RUNNER_TEMP}/busymark-snap-build-start"

- name: Build strict Snap from a clean core24 environment (attempt 1)
id: snapcraft
uses: snapcore/action-build@v1
Expand All @@ -244,11 +257,27 @@ jobs:
id: snap-artifact
env:
PRIMARY_SNAP: ${{ steps.snapcraft.outputs.snap }}
PRIMARY_OUTCOME: ${{ steps.snapcraft.outcome }}
RETRY_SNAP: ${{ steps.snapcraft-retry.outputs.snap }}
RETRY_OUTCOME: ${{ steps.snapcraft-retry.outcome }}
run: |
snap_path="${PRIMARY_SNAP:-$RETRY_SNAP}"
if [ "$PRIMARY_OUTCOME" = success ]; then
snap_path="$PRIMARY_SNAP"
else
test "$PRIMARY_OUTCOME" = failure
test "$RETRY_OUTCOME" = success
snap_path="$RETRY_SNAP"
fi
test -n "$snap_path"
test -f "$snap_path"
test "$snap_path" -nt "${RUNNER_TEMP}/busymark-snap-build-start"
snap_path="$(realpath "$snap_path")"
case "$snap_path" in
"$GITHUB_WORKSPACE"/*.snap) ;;
*) echo "Refusing Snap outside the clean checkout: $snap_path" >&2; exit 1 ;;
esac
test "$(unsquashfs -cat "$snap_path" snap/snapcraft.yaml | sha256sum | cut -d' ' -f1)" = \
"$(sha256sum snap/snapcraft.yaml | cut -d' ' -f1)"
echo "snap=$snap_path" >> "$GITHUB_OUTPUT"

- name: Upload Snap artifact
Expand Down Expand Up @@ -277,6 +306,104 @@ jobs:
"$query" "$loader" | grep -q "\"svg\" 6 \"gdk-pixbuf\""
'

- name: Verify shared runtimes, packaged tools, media, and resources
run: |
snap run --shell busymark -c '
set -eu
inspect_dependencies() {
dependency_object="$1"
if dependency_output="$(ldd "$dependency_object" 2>&1)"; then
:
else
printf "Dependency inspection failed: %s\n%s\n" \
"$dependency_object" "$dependency_output" >&2
exit 1
fi
case "$dependency_output" in
*"not found"*)
printf "Unresolved dependencies: %s\n%s\n" \
"$dependency_object" "$dependency_output" >&2
exit 1
;;
esac
}
require_resolution() {
required_library="$1"
required_path="$2"
resolved_path="$(printf "%s\n" "$dependency_output" | \
awk -v name="$required_library" \
"\$1 == name && \$2 == \"=>\" { print \$3; exit }")"
if [ -z "$resolved_path" ] || \
! expected_real="$(readlink -e "$required_path")" || \
! resolved_real="$(readlink -e "$resolved_path")" || \
[ "$resolved_real" != "$expected_real" ]; then
printf "Unexpected resolution for %s in %s; expected %s\n%s\n" \
"$required_library" "$dependency_object" \
"$required_path" "$dependency_output" >&2
exit 1
fi
}
app_lib="$SNAP/usr/lib/$SNAP_LAUNCHER_ARCH_TRIPLET"
inspect_dependencies "$SNAP/busymark"
for library in \
libhandy-1.so.0 libsecret-1.so.0 libwebkit2gtk-4.1.so.0 \
libgtk-3.so.0 libglib-2.0.so.0 libpango-1.0.so.0; do
test ! -e "$app_lib/$library"
require_resolution "$library" \
"$SNAP_DESKTOP_RUNTIME/usr/lib/$SNAP_LAUNCHER_ARCH_TRIPLET/$library"
done
graphics_lib="$SNAP/gpu-2404/usr/lib/$SNAP_LAUNCHER_ARCH_TRIPLET"
for library in \
libX11.so.6 libXdamage.so.1 libXext.so.6 libXfixes.so.3 \
libxcb-shm.so.0 libxcb.so.1 libwayland-client.so.0 \
libwayland-cursor.so.0 libwayland-egl.so.1; do
test ! -e "$app_lib/$library"
test -e "$graphics_lib/$library"
require_resolution "$library" "$graphics_lib/$library"
done
for helper in WebKitWebProcess WebKitNetworkProcess WebKitGPUProcess; do
test -x "$SNAP_DESKTOP_RUNTIME/usr/lib/$SNAP_LAUNCHER_ARCH_TRIPLET/webkit2gtk-4.1/$helper"
done
test -x "$SNAP/usr/bin/git"
test -x "$SNAP/usr/bin/ssh"
test -x "$SNAP/usr/bin/setsid"
inspect_dependencies "$SNAP/usr/lib/git-core/git-remote-http"
require_resolution libcurl-gnutls.so.4 \
"$app_lib/libcurl-gnutls.so.4"
GIT_EXEC_PATH="$SNAP/usr/lib/git-core" \
"$SNAP/usr/bin/git" --exec-path | grep -Fx "$SNAP/usr/lib/git-core"
"$SNAP/usr/bin/ssh" -V
provider_plugins="$SNAP_DESKTOP_RUNTIME/usr/lib/$SNAP_LAUNCHER_ARCH_TRIPLET/gstreamer-1.0"
for plugin in libgstavi.so libgstisomp4.so libgstmatroska.so \
libgstogg.so libgsttheora.so libgstvpx.so; do
test -f "$provider_plugins/$plugin"
inspect_dependencies "$provider_plugins/$plugin"
done
private_plugins="$SNAP/usr/lib/$SNAP_LAUNCHER_ARCH_TRIPLET/gstreamer-1.0"
for plugin in libgstavi.so libgstisomp4.so libgstmatroska.so \
libgstogg.so libgsttheora.so libgstvpx.so; do
test ! -e "$private_plugins/$plugin"
done
for plugin in libgstlibav.so libgstvideoparsersbad.so \
libgstmpeg2dec.so; do
test -f "$private_plugins/$plugin"
inspect_dependencies "$private_plugins/$plugin"
done
test -L "$SNAP/share/busymark/fonts"
test "$(readlink "$SNAP/share/busymark/fonts")" = \
../../usr/share/fonts/truetype/noto
test -f "$SNAP/share/busymark/fonts/NotoSans-Regular.ttf"
test -f "$SNAP/usr/share/doc/fonts-noto-core/copyright"
test -f "$SNAP/usr/share/doc/fonts-noto-mono/copyright"
for resource_kind in themes icons; do
for resource_name in Yaru Yaru-dark; do
resource="$SNAP/share/$resource_kind/$resource_name"
test -L "$resource"
test -e "$resource"
done
done
'

- name: Install one strict-Snap dictionary and verify refresh/offline use
run: |
report="$HOME/snap/busymark/common/spelling-release.json"
Expand Down
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ for filesystem and Git-integration details.

## Run from source

BusyMark currently uses Flutter 3.47.4. Install the Linux packages required by
BusyMark currently uses Flutter 3.47.5. Install the Linux packages required by
Flutter and BusyMark's CMake configuration:

```bash
Expand Down Expand Up @@ -89,6 +89,31 @@ PDF, and sandbox verification. Those packages are not required for an ordinary
local build; see [the Linux workflow](.github/workflows/flutter-linux.yml) when
reproducing release verification.

## Snap release and security builds

Release, security-refresh, and packaging-change artifacts must be built from
the current `snap/snapcraft.yaml` in a clean Ubuntu 24.04 amd64 Snapcraft build
environment. The authoritative build sequence is:

```bash
snapcraft --version
snapcraft expand-extensions
snapcraft clean
snapcraft
sha256sum ./busymark_*.snap
```

Inspect and test the exact file emitted by the final `snapcraft` command. A
failed build must not fall back to an older `.snap`. The `snap` job in
[the Linux workflow](.github/workflows/flutter-linux.yml) follows this route,
installs that selected artifact, and runs the strict-confinement smoke checks.

`tools/build_install_snap_local.sh` is intentionally different: it replaces
the Flutter payload in an installed Snap scaffold for quick development and
may retain that scaffold's Ubuntu libraries and bundled tools. Even with
`--no-install`, its output is not a dependency refresh and must not be used for
a release, a security update, or validation of `stage-packages` changes.

## Screenshots

<table>
Expand Down
6 changes: 3 additions & 3 deletions docs/development/writerside-toc-implementation.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ parity**; retained safety and integration differences are listed below.

The receiving checkout was clean at `c3b53ff` (BusyMark 0.4.2). The named handoff
implementation points matched the receiving source; no archive was copied over it.
Flutter 3.47.4 and Dart 3.13.2 were used without upgrading dependencies.
Flutter 3.47.5 and Dart 3.13.2 were used without upgrading dependencies.

User decisions: retain BusyMark AI, Git, clipboard, and file commands **after** the
Writerside actions; use installed Writerside **2026.07.8925** for undocumented
Expand Down Expand Up @@ -344,7 +344,7 @@ production controller/services. Only desktop host/theme and persistence services
are isolated. Their framework menu fallback is test-only; these checks do not
replace or claim a new recording of the native GTK evidence above.

Review verification (Flutter 3.47.4 / Dart 3.13.2):
Review verification (Flutter 3.47.5 / Dart 3.13.2):

- Full `flutter test --no-pub --concurrency=2 --file-reporter
json:/tmp/busymark-toc-review-full.json`: exit 0, **1,921 passed, 58 optional
Expand Down Expand Up @@ -404,7 +404,7 @@ remain explicitly documented adaptations. Full original-Writerside parity is
not claimed. Translations are BusyMark translations, not verified original
JetBrains locale strings.

Verification for this follow-up (Flutter 3.47.4 / Dart 3.13.2):
Verification for this follow-up (Flutter 3.47.5 / Dart 3.13.2):

- Full `flutter test --no-pub --concurrency=2 --file-reporter json:/tmp/busymark-title-full.json`:
exit 0, **1,938 passed, 58 optional integration skips**; 17 new regressions.
Expand Down
19 changes: 15 additions & 4 deletions docs/snap-confinement.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,21 @@ Typst, D2, WebKitGTK, and its other runtime dependencies rather than executing
arbitrary host programs.

The application plugs the standard desktop, display, audio, network, `home`,
`removable-media`, and `ssh-keys` interfaces. This supports documentation
projects in ordinary home-directory locations and connected removable media.
Interfaces that expose sensitive SSH material are controlled by snapd and may
require an explicit connection on the installed system.
`removable-media`, `ssh-keys`, and `password-manager-service` interfaces. This
supports documentation projects in ordinary home-directory locations and
connected removable media. Interfaces that expose SSH keys or the system
credential store are not automatically connected. Connect only the access you
need on the installed system:

```bash
sudo snap connect busymark:ssh-keys
sudo snap connect busymark:password-manager-service
```

BusyMark's AI provider keys use the system credential store. Saving, reading,
or removing those keys in a strict Snap requires the second connection. It
grants access to the session's password manager, so make that choice only on a
trusted installation.

## Git author identity

Expand Down
7 changes: 6 additions & 1 deletion lib/l10n/app_ar.arb
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@
"noSpellingSuggestions": "لا توجد اقتراحات إملائية",
"spellingWordCheckFailed": "تعذّر التحقق من هذه الكلمة",
"spellingCheckFailed": "فشل التدقيق الإملائي",
"spellingSettingsLoadFailed": "تعذّر تحميل إعدادات التدقيق الإملائي",
"retrySpellingSettings": "إعادة محاولة تحميل إعدادات التدقيق الإملائي",
"spellingCheckIncomplete": "التدقيق الإملائي غير مكتمل",
"spellingDictionaryNotInstalled": "القاموس غير مثبّت",
"spellingDictionaryNotInstalledForLanguage": "قاموس ⁨{dictionary}⁩ غير مثبّت",
Expand Down Expand Up @@ -3374,5 +3376,8 @@
"renameTopicFileInstead": "تُشتق معرّفات الموضوعات من أسماء ملفاتها. استخدم «إعادة تسمية ملف الموضوع» في جدول المحتويات أو الشريط الجانبي للملفات.",
"diagnosticWritersideWebFileNameInvalid": "ينشر المثيل \"{instanceId}\" اسم ملف ويب غير صالح \"{webFileName}\" للموضوع \"{topic}\".",
"diagnosticWritersideWebFileNameCollision": "ينشر المثيل \"{instanceId}\" الموضوعين \"{firstTopic}\" و\"{secondTopic}\" باسم \"{webFileName}\".",
"spellingDictionaryRecoveryConflict": "تم تغيير قاموس التدقيق الإملائي خارجيًا. تم الاحتفاظ بنسخة استرداد."
"spellingDictionaryRecoveryConflict": "تم تغيير قاموس التدقيق الإملائي خارجيًا. تم الاحتفاظ بنسخة استرداد.",
"windowMinimize": "تصغير",
"windowMaximize": "تكبير",
"windowRestore": "استعادة"
}
7 changes: 6 additions & 1 deletion lib/l10n/app_de.arb
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@
"noSpellingSuggestions": "Keine Rechtschreibvorschläge",
"spellingWordCheckFailed": "Dieses Wort konnte nicht geprüft werden",
"spellingCheckFailed": "Rechtschreibprüfung fehlgeschlagen",
"spellingSettingsLoadFailed": "Rechtschreibeinstellungen konnten nicht geladen werden",
"retrySpellingSettings": "Rechtschreibeinstellungen erneut laden",
"spellingCheckIncomplete": "Rechtschreibprüfung ist unvollständig",
"spellingDictionaryNotInstalled": "Wörterbuch nicht installiert",
"spellingDictionaryNotInstalledForLanguage": "Das Wörterbuch {dictionary} ist nicht installiert",
Expand Down Expand Up @@ -3374,5 +3376,8 @@
"renameTopicFileInstead": "Themen-IDs werden aus ihren Dateinamen abgeleitet. Verwenden Sie „Themendatei umbenennen“ im Inhaltsverzeichnis oder in der Seitenleiste „Dateien“.",
"diagnosticWritersideWebFileNameInvalid": "Instanz \"{instanceId}\" veröffentlicht den ungültigen Webdateinamen \"{webFileName}\" für das Thema \"{topic}\".",
"diagnosticWritersideWebFileNameCollision": "Instanz \"{instanceId}\" veröffentlicht sowohl \"{firstTopic}\" als auch \"{secondTopic}\" unter \"{webFileName}\".",
"spellingDictionaryRecoveryConflict": "Das Rechtschreibwörterbuch wurde extern geändert. Eine Wiederherstellungskopie wurde aufbewahrt."
"spellingDictionaryRecoveryConflict": "Das Rechtschreibwörterbuch wurde extern geändert. Eine Wiederherstellungskopie wurde aufbewahrt.",
"windowMinimize": "Minimieren",
"windowMaximize": "Maximieren",
"windowRestore": "Wiederherstellen"
}
12 changes: 11 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -2768,6 +2768,10 @@
"noSpellingSuggestions": "No spelling suggestions",
"spellingWordCheckFailed": "Could not check this word",
"spellingCheckFailed": "Spelling check failed",
"spellingSettingsLoadFailed": "Could not load spelling settings",
"@spellingSettingsLoadFailed": {"description": "Settings failure shown when a spelling word store cannot be read."},
"retrySpellingSettings": "Retry loading spelling settings",
"@retrySpellingSettings": {"description": "Action that deliberately retries failed spelling settings preparation."},
"spellingCheckIncomplete": "Spelling check is incomplete",
"spellingDictionaryNotInstalled": "Dictionary not installed",
"spellingDictionaryNotInstalledForLanguage": "{dictionary} dictionary is not installed",
Expand Down Expand Up @@ -2802,5 +2806,11 @@
}
}
},
"spellingDictionaryRecoveryConflict": "The spelling dictionary changed externally. A recovery copy was preserved."
"spellingDictionaryRecoveryConflict": "The spelling dictionary changed externally. A recovery copy was preserved.",
"windowMinimize": "Minimize",
"@windowMinimize": {"description": "Tooltip and accessibility label for minimizing the application window."},
"windowMaximize": "Maximize",
"@windowMaximize": {"description": "Tooltip and accessibility label for maximizing the application window."},
"windowRestore": "Restore",
"@windowRestore": {"description": "Tooltip and accessibility label for restoring a maximized or fullscreen window to its normal size."}
}
7 changes: 6 additions & 1 deletion lib/l10n/app_es.arb
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@
"noSpellingSuggestions": "No hay sugerencias ortográficas",
"spellingWordCheckFailed": "No se pudo comprobar esta palabra",
"spellingCheckFailed": "La revisión ortográfica falló",
"spellingSettingsLoadFailed": "No se pudo cargar la configuración ortográfica",
"retrySpellingSettings": "Volver a cargar la configuración ortográfica",
"spellingCheckIncomplete": "La revisión ortográfica está incompleta",
"spellingDictionaryNotInstalled": "Diccionario no instalado",
"spellingDictionaryNotInstalledForLanguage": "El diccionario de {dictionary} no está instalado",
Expand Down Expand Up @@ -3374,5 +3376,8 @@
"renameTopicFileInstead": "Los ID de los temas proceden de sus nombres de archivo. Use «Cambiar nombre del archivo de tema» en la tabla de contenido o en la barra lateral Archivos.",
"diagnosticWritersideWebFileNameInvalid": "La instancia \"{instanceId}\" publica el nombre de archivo web no válido \"{webFileName}\" para el tema \"{topic}\".",
"diagnosticWritersideWebFileNameCollision": "La instancia \"{instanceId}\" publica \"{firstTopic}\" y \"{secondTopic}\" como \"{webFileName}\".",
"spellingDictionaryRecoveryConflict": "El diccionario ortográfico se modificó externamente. Se conservó una copia de recuperación."
"spellingDictionaryRecoveryConflict": "El diccionario ortográfico se modificó externamente. Se conservó una copia de recuperación.",
"windowMinimize": "Minimizar",
"windowMaximize": "Maximizar",
"windowRestore": "Restaurar"
}
7 changes: 6 additions & 1 deletion lib/l10n/app_et.arb
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,8 @@
"noSpellingSuggestions": "Õigekirjasoovitusi pole",
"spellingWordCheckFailed": "Seda sõna ei saanud kontrollida",
"spellingCheckFailed": "Õigekirjakontroll nurjus",
"spellingSettingsLoadFailed": "Õigekirjakontrolli sätteid ei õnnestunud laadida",
"retrySpellingSettings": "Laadi õigekirjakontrolli sätted uuesti",
"spellingCheckIncomplete": "Õigekirjakontroll on pooleli",
"spellingDictionaryNotInstalled": "Sõnastik pole installitud",
"spellingDictionaryNotInstalledForLanguage": "{dictionary} sõnastik pole installitud",
Expand Down Expand Up @@ -3834,5 +3836,8 @@
"renameTopicFileInstead": "Teema ID-d tuletatakse failinimedest. Kasuta sisukorras või failide külgribal käsku „Nimeta teemafail ümber“.",
"diagnosticWritersideWebFileNameInvalid": "Eksemplar \"{instanceId}\" avaldab teema \"{topic}\" jaoks sobimatu veebifailinime \"{webFileName}\".",
"diagnosticWritersideWebFileNameCollision": "Eksemplar \"{instanceId}\" avaldab nii \"{firstTopic}\" kui ka \"{secondTopic}\" nimega \"{webFileName}\".",
"spellingDictionaryRecoveryConflict": "Õigekirjasõnastikku muudeti väliselt. Taastekoopia säilitati."
"spellingDictionaryRecoveryConflict": "Õigekirjasõnastikku muudeti väliselt. Taastekoopia säilitati.",
"windowMinimize": "Minimeeri",
"windowMaximize": "Maksimeeri",
"windowRestore": "Taasta"
}
Loading
Loading