Compare commits
No commits in common. "9099ca3fcb09930e85d356613ada41968b41426d" and "8a8bf653fe992dcfa891aaf976f985df30cd2de4" have entirely different histories.
9099ca3fcb
...
8a8bf653fe
35 changed files with 76 additions and 575 deletions
|
|
@ -31,10 +31,6 @@ builds:
|
|||
- CARGO_TARGET_X86_64_APPLE_DARWIN_RUSTFLAGS=-C link-arg=-Wl,-headerpad_max_install_names
|
||||
- CARGO_TARGET_AARCH64_APPLE_DARWIN_RUSTFLAGS=-C link-arg=-Wl,-headerpad_max_install_names
|
||||
- BINDGEN_EXTRA_CLANG_ARGS={{ if eq .Os "linux" }}-idirafter /sysroot/linux_{{ .Arch }}/usr/include -idirafter /sysroot/linux_{{ .Arch }}/usr/include/{{ if eq .Arch "amd64" }}x86_64{{ else }}aarch64{{ end }}-linux-gnu{{ end }}
|
||||
hooks:
|
||||
# GoReleaser's own edition makes no AppImages, so the script does, for each Linux target.
|
||||
post:
|
||||
- cmd: '{{ if eq .Os "linux" }}packaging/linux/appimage.sh "{{ .Path }}" "{{ .Arch }}" "{{ .Version }}" dist{{ else }}true{{ end }}'
|
||||
|
||||
# The full build for the Mac, which ships only inside the app on the disk image, never in an
|
||||
# archive of its own.
|
||||
|
|
@ -192,7 +188,6 @@ checksum:
|
|||
name_template: checksums.txt
|
||||
extra_files:
|
||||
- glob: dist/*.dmg
|
||||
- glob: dist/*.AppImage
|
||||
|
||||
# A release takes its version from the tag, which make release checks against VERSION. A snapshot
|
||||
# has no tag, so it takes VERSION, which the Makefile passes in.
|
||||
|
|
@ -208,4 +203,3 @@ release:
|
|||
name: midi-harbor
|
||||
extra_files:
|
||||
- glob: dist/*.dmg
|
||||
- glob: dist/*.AppImage
|
||||
|
|
|
|||
|
|
@ -229,4 +229,5 @@ stops when the user quits entirely (feature 014-mac-app-store-mode, research R-0
|
|||
stays a separate process, so a crash in the window still disturbs no connection.
|
||||
|
||||
Amendment 1.4.1 (2026-09-27): wording only. The specifications were split into one per
|
||||
capability and renumbered by topic, and the features named above cite the current specs.
|
||||
capability and renumbered by topic, so the features named above are cited by their new names:
|
||||
the Windows port was 002-windows-support and the App Store mode 003-mac-app-store-mode.
|
||||
|
|
|
|||
74
Cargo.lock
generated
74
Cargo.lock
generated
|
|
@ -21,7 +21,7 @@ checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
|
|||
[[package]]
|
||||
name = "accesskit"
|
||||
version = "0.22.0"
|
||||
source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912"
|
||||
source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f"
|
||||
dependencies = [
|
||||
"uuid",
|
||||
]
|
||||
|
|
@ -29,7 +29,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "accesskit_atspi_common"
|
||||
version = "0.15.0"
|
||||
source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912"
|
||||
source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer",
|
||||
|
|
@ -41,7 +41,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "accesskit_consumer"
|
||||
version = "0.32.0"
|
||||
source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912"
|
||||
source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"hashbrown 0.16.1",
|
||||
|
|
@ -50,7 +50,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "accesskit_macos"
|
||||
version = "0.23.0"
|
||||
source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912"
|
||||
source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer",
|
||||
|
|
@ -63,7 +63,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "accesskit_unix"
|
||||
version = "0.18.0"
|
||||
source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912"
|
||||
source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_atspi_common",
|
||||
|
|
@ -78,7 +78,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "accesskit_windows"
|
||||
version = "0.30.0"
|
||||
source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912"
|
||||
source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_consumer",
|
||||
|
|
@ -91,7 +91,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "accesskit_winit"
|
||||
version = "0.30.0"
|
||||
source = "git+https://github.com/wash2/accesskit?rev=6c20249#6c20249d3b9fd45e7002efad9f6b6171a9257912"
|
||||
source = "git+https://github.com/wash2/accesskit?tag=cosmic-0.14#f0599eed5f18111228266fe3f28991cc48b5964f"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_macos",
|
||||
|
|
@ -884,7 +884,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "build_helpers"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"cfg_aliases",
|
||||
]
|
||||
|
|
@ -1282,7 +1282,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "cosmic-config"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"atomicwrites",
|
||||
"cosmic-config-derive",
|
||||
|
|
@ -1303,7 +1303,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "cosmic-config-derive"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
|
|
@ -1372,7 +1372,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "cosmic-theme"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"almost",
|
||||
"configparser",
|
||||
|
|
@ -1855,7 +1855,7 @@ checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
|||
[[package]]
|
||||
name = "dpi"
|
||||
version = "0.1.2"
|
||||
source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429"
|
||||
source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#71ce08c043814514a8fd92d9d0599f115ae854e8"
|
||||
|
||||
[[package]]
|
||||
name = "drm"
|
||||
|
|
@ -2793,7 +2793,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"build_helpers",
|
||||
"dnd",
|
||||
|
|
@ -2815,7 +2815,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_accessibility"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"accesskit",
|
||||
"accesskit_winit",
|
||||
|
|
@ -2824,7 +2824,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_core"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"build_helpers",
|
||||
|
|
@ -2851,7 +2851,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_debug"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"iced_core",
|
||||
"iced_futures",
|
||||
|
|
@ -2861,7 +2861,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_futures"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"iced_core",
|
||||
|
|
@ -2875,7 +2875,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_graphics"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"bytemuck",
|
||||
|
|
@ -2896,7 +2896,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_program"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"iced_graphics",
|
||||
"iced_runtime",
|
||||
|
|
@ -2905,7 +2905,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_renderer"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"iced_graphics",
|
||||
"iced_tiny_skia",
|
||||
|
|
@ -2917,7 +2917,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_runtime"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"build_helpers",
|
||||
"bytes",
|
||||
|
|
@ -2934,7 +2934,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_tiny_skia"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"cosmic-text",
|
||||
|
|
@ -2951,7 +2951,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_wgpu"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"as-raw-xcb-connection",
|
||||
"bitflags 2.13.2",
|
||||
|
|
@ -2983,7 +2983,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_widget"
|
||||
version = "0.14.2"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"build_helpers",
|
||||
"cosmic-client-toolkit",
|
||||
|
|
@ -3003,7 +3003,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "iced_winit"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"build_helpers",
|
||||
"cosmic-client-toolkit",
|
||||
|
|
@ -3513,7 +3513,7 @@ checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
|
|||
[[package]]
|
||||
name = "libcosmic"
|
||||
version = "1.0.0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=03d7dcb832bc#03d7dcb832bc6d15bc1bb07327a1aa426b2d5bc0"
|
||||
source = "git+https://github.com/pop-os/libcosmic.git?rev=87ab8179#87ab8179e1bd9880239c340855ae8862034bd0e8"
|
||||
dependencies = [
|
||||
"apply",
|
||||
"ashpd 0.12.3",
|
||||
|
|
@ -7724,7 +7724,7 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650"
|
|||
[[package]]
|
||||
name = "winit"
|
||||
version = "0.31.0-beta.2"
|
||||
source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429"
|
||||
source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#71ce08c043814514a8fd92d9d0599f115ae854e8"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"cfg_aliases",
|
||||
|
|
@ -7750,7 +7750,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "winit-android"
|
||||
version = "0.31.0-beta.2"
|
||||
source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429"
|
||||
source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#71ce08c043814514a8fd92d9d0599f115ae854e8"
|
||||
dependencies = [
|
||||
"android-activity",
|
||||
"bitflags 2.13.2",
|
||||
|
|
@ -7765,7 +7765,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "winit-appkit"
|
||||
version = "0.31.0-beta.2"
|
||||
source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429"
|
||||
source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#71ce08c043814514a8fd92d9d0599f115ae854e8"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"block2 0.6.2",
|
||||
|
|
@ -7787,7 +7787,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "winit-common"
|
||||
version = "0.31.0-beta.2"
|
||||
source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429"
|
||||
source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#71ce08c043814514a8fd92d9d0599f115ae854e8"
|
||||
dependencies = [
|
||||
"memmap2 0.9.11",
|
||||
"objc2 0.6.4",
|
||||
|
|
@ -7802,7 +7802,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "winit-core"
|
||||
version = "0.31.0-beta.2"
|
||||
source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429"
|
||||
source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#71ce08c043814514a8fd92d9d0599f115ae854e8"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"cursor-icon",
|
||||
|
|
@ -7816,7 +7816,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "winit-orbital"
|
||||
version = "0.31.0-beta.2"
|
||||
source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429"
|
||||
source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#71ce08c043814514a8fd92d9d0599f115ae854e8"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"dpi",
|
||||
|
|
@ -7832,7 +7832,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "winit-uikit"
|
||||
version = "0.31.0-beta.2"
|
||||
source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429"
|
||||
source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#71ce08c043814514a8fd92d9d0599f115ae854e8"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"block2 0.6.2",
|
||||
|
|
@ -7852,7 +7852,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "winit-wayland"
|
||||
version = "0.31.0-beta.2"
|
||||
source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429"
|
||||
source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#71ce08c043814514a8fd92d9d0599f115ae854e8"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"bitflags 2.13.2",
|
||||
|
|
@ -7878,7 +7878,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "winit-web"
|
||||
version = "0.31.0-beta.2"
|
||||
source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429"
|
||||
source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#71ce08c043814514a8fd92d9d0599f115ae854e8"
|
||||
dependencies = [
|
||||
"atomic-waker",
|
||||
"bitflags 2.13.2",
|
||||
|
|
@ -7900,7 +7900,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "winit-win32"
|
||||
version = "0.31.0-beta.2"
|
||||
source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429"
|
||||
source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#71ce08c043814514a8fd92d9d0599f115ae854e8"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"cursor-icon",
|
||||
|
|
@ -7916,7 +7916,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "winit-x11"
|
||||
version = "0.31.0-beta.2"
|
||||
source = "git+https://github.com/pop-os/winit.git?rev=9567503#9567503d34130113e36b0167a2b8829c3756e429"
|
||||
source = "git+https://github.com/pop-os/winit.git?tag=cosmic-0.14#71ce08c043814514a8fd92d9d0599f115ae854e8"
|
||||
dependencies = [
|
||||
"bitflags 2.13.2",
|
||||
"bytemuck",
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ leaves out the graphical interface, for machines without a display.
|
|||
```bash
|
||||
sudo apt install ./midi-harbor_<version>_<arch>.deb # Debian and Ubuntu
|
||||
sudo dnf install ./midi-harbor-<version>-1.<arch>.rpm # Fedora and RHEL
|
||||
chmod +x Midi-Harbor-<version>-x86_64.AppImage # any other distribution
|
||||
```
|
||||
|
||||
The release builds need glibc 2.35 or newer, so RHEL 9 and its rebuilds must build from source.
|
||||
|
|
|
|||
|
|
@ -27,19 +27,17 @@ tracing.workspace = true
|
|||
# deliberate and re-verified by hand (research R-001).
|
||||
[target.'cfg(target_os = "macos")'.dependencies.libcosmic]
|
||||
git = "https://github.com/pop-os/libcosmic.git"
|
||||
rev = "03d7dcb832bc"
|
||||
rev = "87ab8179"
|
||||
default-features = false
|
||||
features = ["winit", "wgpu", "tokio", "multi-window"]
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies.libcosmic]
|
||||
git = "https://github.com/pop-os/libcosmic.git"
|
||||
rev = "03d7dcb832bc"
|
||||
rev = "87ab8179"
|
||||
features = ["winit", "wgpu", "tokio", "multi-window", "wayland"]
|
||||
|
||||
# Without `a11y` no accesskit crate is built here, so the accesskit_winit fix newer libcosmic needs
|
||||
# on Windows does not apply; enabling it would need the override in R-001.
|
||||
[target.'cfg(windows)'.dependencies.libcosmic]
|
||||
git = "https://github.com/pop-os/libcosmic.git"
|
||||
rev = "03d7dcb832bc"
|
||||
rev = "87ab8179"
|
||||
default-features = false
|
||||
features = ["winit", "wgpu", "tokio", "multi-window"]
|
||||
|
|
|
|||
|
|
@ -784,7 +784,7 @@ impl cosmic::Application for App {
|
|||
activity_filter.insert().text("Problems only");
|
||||
activity_filter.activate_position(0);
|
||||
|
||||
let mut app = App {
|
||||
let app = App {
|
||||
core,
|
||||
nav,
|
||||
page: Page::Endpoints,
|
||||
|
|
@ -810,12 +810,6 @@ impl cosmic::Application for App {
|
|||
// have filled.
|
||||
let connect =
|
||||
cosmic::task::future(async { Message::Connected(Client::connect(socket).await) });
|
||||
// The window's title, which taskbars and window lists show; libcosmic leaves it empty,
|
||||
// and the header draws its own.
|
||||
let connect = match app.core.main_window_id() {
|
||||
Some(id) => Task::batch([connect, app.set_window_title("Midi Harbor".to_owned(), id)]),
|
||||
None => connect,
|
||||
};
|
||||
// The menus as a message rather than now: the window is set up before the event loop
|
||||
// runs, and winit builds its default menu once it does, over anything installed earlier.
|
||||
#[cfg(target_os = "macos")]
|
||||
|
|
|
|||
|
|
@ -91,25 +91,12 @@ pub struct ServiceSpec {
|
|||
|
||||
impl ServiceSpec {
|
||||
/// Builds a specification running the current executable as the daemon.
|
||||
///
|
||||
/// From an AppImage it runs the AppImage file, since the running executable is inside a
|
||||
/// mount that is gone once this process exits.
|
||||
pub fn for_current_executable() -> Result<Self, ServiceError> {
|
||||
let running = std::env::current_exe().map_err(|source| ServiceError::Io {
|
||||
let executable = std::env::current_exe().map_err(|source| ServiceError::Io {
|
||||
operation: "locate",
|
||||
path: PathBuf::from("the running executable"),
|
||||
source,
|
||||
})?;
|
||||
// AppImages exist only on Linux.
|
||||
#[cfg(target_os = "linux")]
|
||||
let executable = appimage_file(
|
||||
&running,
|
||||
std::env::var_os("APPIMAGE"),
|
||||
std::env::var_os("APPDIR"),
|
||||
)
|
||||
.unwrap_or(running);
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
let executable = running;
|
||||
Ok(Self {
|
||||
executable,
|
||||
arguments: vec!["daemon".to_owned()],
|
||||
|
|
@ -204,25 +191,6 @@ pub fn detect() -> Result<Box<dyn ServiceManager>, ServiceError> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the AppImage file the running executable was started from, if it was.
|
||||
///
|
||||
/// The AppImage runtime sets `APPIMAGE` to the file and `APPDIR` to where it mounted it, and
|
||||
/// every process started from the application inherits both. So the executable must be inside
|
||||
/// `APPDIR`, or the variables belong to another AppImage that started this program.
|
||||
#[cfg(target_os = "linux")]
|
||||
fn appimage_file(
|
||||
running: &Path,
|
||||
appimage: Option<std::ffi::OsString>,
|
||||
appdir: Option<std::ffi::OsString>,
|
||||
) -> Option<PathBuf> {
|
||||
let appdir = PathBuf::from(appdir?);
|
||||
if appdir.as_os_str().is_empty() || !running.starts_with(&appdir) {
|
||||
return None;
|
||||
}
|
||||
let appimage = PathBuf::from(appimage?);
|
||||
appimage.is_absolute().then_some(appimage)
|
||||
}
|
||||
|
||||
/// Reports whether the executable a registration points at still exists.
|
||||
pub(crate) fn is_stale(registered: Option<&Path>) -> bool {
|
||||
registered.is_some_and(|path| !path.exists())
|
||||
|
|
@ -267,65 +235,3 @@ pub(crate) fn run(command: &str, args: &[&str]) -> Result<String, ServiceError>
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
// AppImages exist only on Linux, and these paths are not absolute on Windows.
|
||||
#[cfg(all(test, target_os = "linux"))]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
/// Locks which executable a service registration names when the program runs from an
|
||||
/// AppImage.
|
||||
///
|
||||
/// The AppImage runtime (AppImage/type2-runtime, runtime.c) mounts the image at
|
||||
/// `/tmp/.mount_<name><random>`, sets `APPDIR` to that mount and `APPIMAGE` to the image
|
||||
/// file, and replaces itself with the application. The mount is gone once the application
|
||||
/// exits, so a unit naming the executable inside it fails at the next login. Children inherit
|
||||
/// both variables, so a program started from another AppImage, such as a terminal, sees that
|
||||
/// AppImage's values while running from somewhere else entirely.
|
||||
#[test]
|
||||
fn a_registration_names_the_appimage_file_only_when_running_from_its_mount() {
|
||||
let mount = "/tmp/.mount_MidiHaAbC123";
|
||||
let inside = "/tmp/.mount_MidiHaAbC123/usr/bin/midi-harbor";
|
||||
let cases = [
|
||||
(
|
||||
"running from the AppImage's mount",
|
||||
inside,
|
||||
Some("/home/user/Apps/Midi-Harbor-0.1.0-x86_64.AppImage"),
|
||||
Some(mount),
|
||||
Some("/home/user/Apps/Midi-Harbor-0.1.0-x86_64.AppImage"),
|
||||
),
|
||||
(
|
||||
"installed binary started from another AppImage's terminal",
|
||||
"/usr/bin/midi-harbor",
|
||||
Some("/home/user/Apps/Terminal.AppImage"),
|
||||
Some("/tmp/.mount_TerminXyZ789"),
|
||||
None,
|
||||
),
|
||||
(
|
||||
"installed binary with no AppImage involved",
|
||||
"/usr/bin/midi-harbor",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
),
|
||||
(
|
||||
"an empty APPDIR, which every path starts with",
|
||||
inside,
|
||||
Some("/home/user/Apps/Midi-Harbor-0.1.0-x86_64.AppImage"),
|
||||
Some(""),
|
||||
None,
|
||||
),
|
||||
];
|
||||
for (name, running, appimage, appdir, want) in cases {
|
||||
assert_eq!(
|
||||
appimage_file(
|
||||
Path::new(running),
|
||||
appimage.map(Into::into),
|
||||
appdir.map(Into::into),
|
||||
),
|
||||
want.map(PathBuf::from),
|
||||
"{name}: the registration must name a file that outlives this process"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,10 +21,6 @@ fn quote(value: &str) -> String {
|
|||
///
|
||||
/// `Restart=always` is what brings the daemon back after a crash, and `WantedBy=default.target`
|
||||
/// is what starts it at login. Both are required by the resilience rules.
|
||||
///
|
||||
/// `ExecStop` stops the daemon alone and waits for it before systemd signals the rest of the
|
||||
/// unit. Run from an AppImage, the rest is the runtime serving the daemon's own executable, and
|
||||
/// signalled together the mount goes while the daemon is still releasing its notes (R-104).
|
||||
pub fn render_unit(spec: &ServiceSpec) -> String {
|
||||
let mut command = quote(&spec.executable.display().to_string());
|
||||
for argument in &spec.arguments {
|
||||
|
|
@ -47,7 +43,6 @@ pub fn render_unit(spec: &ServiceSpec) -> String {
|
|||
[Service]\n\
|
||||
Type=simple\n\
|
||||
ExecStart={command}\n\
|
||||
ExecStop=/bin/sh -c 'kill -TERM $MAINPID && while kill -0 $MAINPID 2>/dev/null; do sleep 0.1; done'\n\
|
||||
Restart=always\n\
|
||||
RestartSec=2\n\
|
||||
\n{install}"
|
||||
|
|
@ -159,14 +154,11 @@ mod tests {
|
|||
use super::*;
|
||||
|
||||
/// Locks the unit directives systemd acts on: `Restart=always` in every case, so a crashed
|
||||
/// daemon comes back; `After=network.target sound.target`, so it starts once ALSA is up; an
|
||||
/// `ExecStop` that signals the main process alone and waits for it; and an `[Install]` section
|
||||
/// with `WantedBy=default.target` only when it should start at login.
|
||||
/// daemon comes back; `After=network.target sound.target`, so it starts once ALSA is up; and
|
||||
/// an `[Install]` section with `WantedBy=default.target` only when it should start at login.
|
||||
///
|
||||
/// The directives are as systemd.service(5) and systemd.unit(5) define them. Started before
|
||||
/// ALSA is up, every endpoint would fail its first attempt. Without the `ExecStop`, systemd
|
||||
/// signals every process in the unit at once, and a daemon run from an AppImage died of
|
||||
/// SIGBUS as it stopped, its executable unmounted under it (R-104).
|
||||
/// ALSA is up, every endpoint would fail its first attempt.
|
||||
#[test]
|
||||
fn the_unit_restarts_after_a_crash_and_starts_at_login_only_when_asked() {
|
||||
let cases = [
|
||||
|
|
@ -187,13 +179,6 @@ mod tests {
|
|||
unit.contains("\nAfter=network.target sound.target\n"),
|
||||
"{name}: the daemon must wait for the network and the sound stack"
|
||||
);
|
||||
assert!(
|
||||
unit.contains(
|
||||
"\nExecStop=/bin/sh -c 'kill -TERM $MAINPID && while kill -0 $MAINPID \
|
||||
2>/dev/null; do sleep 0.1; done'\n"
|
||||
),
|
||||
"{name}: the daemon must be stopped and waited for before the rest of the unit"
|
||||
);
|
||||
assert_eq!(
|
||||
unit.contains("\n[Install]\nWantedBy=default.target\n"),
|
||||
want_install,
|
||||
|
|
|
|||
|
|
@ -35,18 +35,6 @@ anything newer. On RHEL 9 and its rebuilds, build from source. The packages reco
|
|||
BlueZ. Installing a package does not start anything. Each user who wants the daemon at login runs
|
||||
`midi-harbor service install --start`.
|
||||
|
||||
**Any other Linux distribution**: `Midi-Harbor-<version>-<x86_64|aarch64>.AppImage` runs without
|
||||
installing. Keep it where it will stay, such as `~/Applications`, make it executable with
|
||||
`chmod +x`, and open it; it shows the graphical interface, which offers to start the daemon at
|
||||
login. The service runs the AppImage file itself, so moving a newer one over it updates the
|
||||
daemon at its next start; copying into it fails while the daemon runs. A newer one saved under
|
||||
another name leaves the registration stale until it is opened and set up again. The AppImage
|
||||
needs FUSE, which desktop distributions include, and a desktop's own libraries: ALSA, D-Bus and
|
||||
libxkbcommon. It carries the Avahi client libraries, so it still browses and advertises sessions
|
||||
where only the Avahi daemon is installed. Keep it away from AppImage's portable mode: a `.home` or
|
||||
`.config` directory beside the file moves where the service is registered, and systemd never
|
||||
finds it there.
|
||||
|
||||
**Windows**: unpack `midi-harbor-<version>.windows-amd64.zip` somewhere it will stay, such as
|
||||
`%LOCALAPPDATA%\Programs\Midi Harbor`; the service remembers where it is. There is no installer.
|
||||
The executable is not signed, so the first time it runs Windows may show "Windows protected your
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ image that adds Rust to GoReleaser's cross-compiling image. It builds:
|
|||
| `tar.gz` archive, headless only | Linux and macOS, x86_64 and arm64 |
|
||||
| `zip` archive | Windows x86_64 |
|
||||
| `.deb` and `.rpm`, `midi-harbor` and `midi-harbor-headless` | Linux x86_64 and arm64 |
|
||||
| AppImage, with the graphical interface | Linux x86_64 and arm64 |
|
||||
| `.dmg` holding `Midi Harbor.app`, the license and the docs | macOS, one universal binary |
|
||||
| `checksums.txt` | all of them |
|
||||
|
||||
|
|
@ -60,19 +59,6 @@ script renders to `MenuBarIcon.png`; keep it in step with `Icon.svg` by hand.
|
|||
The Windows executable carries the icon as a resource, compiled by `windres` from mingw-w64, or
|
||||
`rc.exe` when building with MSVC. A Windows build fails without one.
|
||||
|
||||
## AppImage
|
||||
|
||||
GoReleaser makes no AppImages, so `packaging/linux/appimage.sh` makes one from each full Linux
|
||||
binary, as `dist/Midi-Harbor-<version>-<x86_64|aarch64>.AppImage`, and the release publishes it.
|
||||
The image has `patchelf`, `appimagetool` and the AppImage runtime for both architectures, so the
|
||||
arm64 AppImage builds on an x86_64 machine and the other way round.
|
||||
|
||||
The AppImage carries the binary, the desktop entry, the icon, the docs, and the Avahi client
|
||||
libraries from the Debian 12 sysroot with their license; the binary finds those through its
|
||||
RUNPATH. Its `AppRun`, `packaging/linux/AppRun`, starts the binary as `midi-harbor`, which on X11
|
||||
is the window's class and what the desktop entry's `StartupWMClass` names. Everything else comes from the host, as with the packages. Its license comes from the
|
||||
sysroot too, so after updating from a version without AppImages, run `make build-sysroot` again.
|
||||
|
||||
## macOS app and disk image
|
||||
|
||||
GoReleaser makes app bundles and disk images only in its paid edition, so
|
||||
|
|
|
|||
|
|
@ -51,38 +51,3 @@ RUN apt-get update; \
|
|||
cmake --build /tmp/libdmg/build --target dmg-bin -j "$(nproc)"; \
|
||||
install -m 0755 /tmp/libdmg/build/dmg/dmg /usr/local/bin/dmg; \
|
||||
rm -rf /tmp/rcodesign.tar.gz "/tmp/${name}" /tmp/libdmg
|
||||
|
||||
# packaging/linux/appimage.sh builds the AppImages with these: patchelf points the binary at the
|
||||
# libraries bundled beside it, and appimagetool packs the tree behind the AppImage runtime for each
|
||||
# target architecture, running file to read each binary's. appimagetool is itself an AppImage,
|
||||
# unpacked here because a container has no FUSE to mount it with.
|
||||
ARG APPIMAGETOOL_VERSION=1.9.1
|
||||
ARG APPIMAGE_RUNTIME_VERSION=20251108
|
||||
RUN apt-get update; \
|
||||
apt-get --no-install-recommends -y -q install file patchelf; \
|
||||
rm -rf /var/lib/apt/lists/*; \
|
||||
arch=$(uname -m); \
|
||||
case "$arch" in \
|
||||
aarch64) sum=f0837e7448a0c1e4e650a93bb3e85802546e60654ef287576f46c71c126a9158 ;; \
|
||||
x86_64) sum=ed4ce84f0d9caff66f50bcca6ff6f35aae54ce8135408b3fa33abfc3cb384eb0 ;; \
|
||||
esac; \
|
||||
curl --proto '=https' --tlsv1.2 -sSfL -o /tmp/appimagetool.AppImage \
|
||||
"https://github.com/AppImage/appimagetool/releases/download/${APPIMAGETOOL_VERSION}/appimagetool-${arch}.AppImage"; \
|
||||
echo "$sum /tmp/appimagetool.AppImage" | sha256sum -c -; \
|
||||
chmod +x /tmp/appimagetool.AppImage; \
|
||||
cd /opt; \
|
||||
/tmp/appimagetool.AppImage --appimage-extract > /dev/null; \
|
||||
mv squashfs-root appimagetool; \
|
||||
chmod -R a+rX /opt/appimagetool; \
|
||||
ln -s /opt/appimagetool/AppRun /usr/local/bin/appimagetool; \
|
||||
rm /tmp/appimagetool.AppImage; \
|
||||
mkdir -p /usr/local/share/appimage; \
|
||||
for pair in \
|
||||
x86_64:2fca8b443c92510f1483a883f60061ad09b46b978b2631c807cd873a47ec260d \
|
||||
aarch64:00cbdfcf917cc6c0ff6d3347d59e0ca1f7f45a6df1a428a0d6d8a78664d87444; do \
|
||||
runtime="/usr/local/share/appimage/runtime-${pair%%:*}"; \
|
||||
curl --proto '=https' --tlsv1.2 -sSfL -o "$runtime" \
|
||||
"https://github.com/AppImage/type2-runtime/releases/download/${APPIMAGE_RUNTIME_VERSION}/runtime-${pair%%:*}"; \
|
||||
echo "${pair#*:} $runtime" | sha256sum -c -; \
|
||||
chmod 0644 "$runtime"; \
|
||||
done
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
#!/bin/bash
|
||||
# Starts Midi Harbor from inside its AppImage, under the name midi-harbor.
|
||||
#
|
||||
# The AppImage runtime passes the AppImage's own path as argv[0], and on X11 the window's class
|
||||
# is taken from argv[0], so without this the class would be the AppImage's file name and no
|
||||
# desktop would pair the window with its entry, whose StartupWMClass is midi-harbor. exec keeps
|
||||
# the process, so systemd's main process and the runtime's mount stay as they are.
|
||||
exec -a midi-harbor "${0%/*}/usr/bin/midi-harbor" "$@"
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
#!/bin/sh
|
||||
# Wraps a built Linux binary into an AppImage.
|
||||
#
|
||||
# packaging/linux/appimage.sh <binary> <amd64|arm64> <version> <output directory>
|
||||
#
|
||||
# The AppImage is Midi-Harbor-<version>-<x86_64|aarch64>.AppImage in the output directory. It
|
||||
# needs patchelf, appimagetool, the AppImage runtime for the target in
|
||||
# /usr/local/share/appimage/runtime-<x86_64|aarch64>, and the Debian 12 sysroot the binary was
|
||||
# linked against in /sysroot/linux_<arch>; the image packaging/cross/Dockerfile makes has the
|
||||
# tools, and GoReleaser runs it there with the sysroots mounted. MIDI_HARBOR_SYSROOT names another
|
||||
# directory holding the sysroots.
|
||||
#
|
||||
# Started with no arguments the AppImage opens the graphical interface, and `service install` run
|
||||
# from it registers the AppImage file itself, so systemd mounts it again at every start
|
||||
# (specs/017-appimage).
|
||||
#
|
||||
# It bundles only the Avahi client libraries, which a desktop may lack. glibc, ALSA, D-Bus and
|
||||
# libxkbcommon come from the host: a bundled libasound finds no sound cards, and the host's
|
||||
# libxkbcommon-x11, which the interface loads while it runs, is built against the host's
|
||||
# libxkbcommon (R-104).
|
||||
set -eu
|
||||
|
||||
binary=$1
|
||||
arch=$2
|
||||
version=$3
|
||||
out=$4
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
case "$arch" in
|
||||
amd64) machine=x86_64 ;;
|
||||
arm64) machine=aarch64 ;;
|
||||
*)
|
||||
echo "appimage.sh: unsupported architecture $arch" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
sysroot="${MIDI_HARBOR_SYSROOT:-/sysroot}/linux_$arch"
|
||||
libraries="$sysroot/usr/lib/$machine-linux-gnu"
|
||||
runtime="/usr/local/share/appimage/runtime-$machine"
|
||||
appdir="$out/appimage-$arch/AppDir"
|
||||
image="$out/Midi-Harbor-$version-$machine.AppImage"
|
||||
id=com.mrgeckosmedia.MidiHarbor
|
||||
|
||||
# Check the sysroot is new enough to carry the bundled libraries' license.
|
||||
if [ ! -f "$sysroot/usr/share/doc/libavahi-client3/copyright" ]; then
|
||||
echo "appimage.sh: $sysroot has no Avahi license; run make build-sysroot" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Assemble the tree. AppRun is what the runtime starts; it runs the binary under the name the
|
||||
# desktop entry expects.
|
||||
rm -rf "$appdir" "$image"
|
||||
mkdir -p "$appdir/usr/bin" "$appdir/usr/lib" "$appdir/usr/share/applications" \
|
||||
"$appdir/usr/share/icons/hicolor/scalable/apps" "$appdir/usr/share/doc/midi-harbor" \
|
||||
"$appdir/usr/share/doc/libavahi-client3"
|
||||
cp "$binary" "$appdir/usr/bin/midi-harbor"
|
||||
chmod 0755 "$appdir/usr/bin/midi-harbor"
|
||||
cp packaging/linux/AppRun "$appdir/AppRun"
|
||||
chmod 0755 "$appdir/AppRun"
|
||||
|
||||
# Bundle the Avahi client libraries, found through the binary's RUNPATH rather than
|
||||
# LD_LIBRARY_PATH, which would reach every command the daemon runs.
|
||||
cp -L "$libraries/libavahi-client.so.3" "$libraries/libavahi-common.so.3" "$appdir/usr/lib/"
|
||||
patchelf --set-rpath '$ORIGIN/../lib' "$appdir/usr/bin/midi-harbor"
|
||||
|
||||
# Desktop entry and icon, at the root where appimagetool looks and under usr/share where desktop
|
||||
# integration tools do.
|
||||
cp packaging/linux/$id.desktop "$appdir/$id.desktop"
|
||||
cp packaging/linux/$id.desktop "$appdir/usr/share/applications/$id.desktop"
|
||||
cp Icon.svg "$appdir/$id.svg"
|
||||
cp Icon.svg "$appdir/usr/share/icons/hicolor/scalable/apps/$id.svg"
|
||||
|
||||
# Licenses and documentation.
|
||||
cp LICENSE.txt "$appdir/usr/share/doc/midi-harbor/copyright"
|
||||
cp docs/*.md "$appdir/usr/share/doc/midi-harbor/"
|
||||
cp "$sysroot/usr/share/doc/libavahi-client3/copyright" "$appdir/usr/share/doc/libavahi-client3/"
|
||||
cp "$sysroot/usr/share/common-licenses/LGPL-2.1" "$appdir/usr/share/doc/libavahi-client3/"
|
||||
|
||||
# Pack it behind the runtime for the target.
|
||||
ARCH=$machine appimagetool --no-appstream --runtime-file "$runtime" "$appdir" "$image"
|
||||
rm -rf "$out/appimage-$arch"
|
||||
|
|
@ -4,8 +4,7 @@ Name=Midi Harbor
|
|||
Comment=Virtual MIDI ports, hardware, network sessions and Bluetooth MIDI that reconnect by themselves
|
||||
Exec=midi-harbor gui
|
||||
Icon=com.mrgeckosmedia.MidiHarbor
|
||||
# On X11 the window's class is argv[0]'s file name; Wayland pairs by the application ID instead.
|
||||
StartupWMClass=midi-harbor
|
||||
StartupWMClass=com.mrgeckosmedia.MidiHarbor
|
||||
Terminal=false
|
||||
Categories=AudioVideo;Audio;Midi;
|
||||
Keywords=MIDI;RTP-MIDI;Network MIDI;Bluetooth;
|
||||
|
|
|
|||
|
|
@ -209,9 +209,7 @@ notarize() {
|
|||
fi
|
||||
xcrun stapler staple "$1" >&2
|
||||
else
|
||||
# An hour rather than rcodesign's ten minutes: Apple holds a team's first submissions for
|
||||
# longer, and a release that stops waiting is left unnotarized.
|
||||
rcodesign notary-submit --api-key-file "$api_key" --max-wait-seconds 3600 --staple "$1" >&2
|
||||
rcodesign notary-submit --api-key-file "$api_key" --staple "$1" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,3 @@ RUN find /usr/lib -type l -lname '/*' | while read -r link; do \
|
|||
FROM scratch
|
||||
COPY --from=debian /usr/include /usr/include
|
||||
COPY --from=debian /usr/lib /usr/lib
|
||||
# The AppImage carries the Avahi client libraries, so it carries their license beside them.
|
||||
COPY --from=debian /usr/share/doc/libavahi-client3/copyright /usr/share/doc/libavahi-client3/copyright
|
||||
COPY --from=debian /usr/share/common-licenses/LGPL-2.1 /usr/share/common-licenses/LGPL-2.1
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Contract: Command-Line Interface
|
||||
|
||||
**Feature**: 001-service-and-clients | **Version**: 1.0 | **Date**: 2026-09-20
|
||||
**Feature**: 001-service-and-clients, first written as 001-midi-connectivity-manager | **Version**: 1.0 | **Date**: 2026-09-20
|
||||
|
||||
The user-facing contract for the `midi-harbor` executable. Governed by FR-039 through FR-039h and
|
||||
FR-042. Every command here is a thin projection of the [IPC protocol](./ipc-protocol.md); the CLI
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Contract: Daemon gRPC Protocol
|
||||
|
||||
**Feature**: 001-service-and-clients | **Protocol**: `midiharbor.v1` | **Date**: 2026-09-20
|
||||
**Feature**: 001-service-and-clients, first written as 001-midi-connectivity-manager | **Protocol**: `midiharbor.v1` | **Date**: 2026-09-20
|
||||
|
||||
The contract between `midi-harbor daemon` and its clients (the GUI and the CLI). Governed by
|
||||
FR-036, FR-037, FR-039c, FR-040, FR-041 and Constitution Principle II.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Data Model: Midi Harbor
|
||||
|
||||
**Feature**: 001-service-and-clients | **Date**: 2026-09-20
|
||||
**Feature**: 001-service-and-clients, first written as 001-midi-connectivity-manager | **Date**: 2026-09-20
|
||||
|
||||
This document defines the domain entities, their fields, relationships, validation rules, and
|
||||
state transitions. It is the vocabulary shared by the daemon, the IPC contract, the CLI, and the
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Implementation Plan: Midi Harbor — MIDI Connectivity Manager
|
||||
|
||||
**Date**: 2026-09-20 | **Spec**: [spec.md](./spec.md)
|
||||
**Branch**: `001-midi-connectivity-manager`, this spec's original name | **Date**: 2026-09-20 | **Spec**: [spec.md](./spec.md)
|
||||
|
||||
**Input**: Feature specification from `/specs/001-service-and-clients/spec.md`
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ decisions strengthened compliance rather than eroding it:
|
|||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/001-service-and-clients/
|
||||
specs/001-service-and-clients/ # first written as 001-midi-connectivity-manager
|
||||
├── plan.md # This file, the architecture of the whole project
|
||||
├── spec.md # The service and its clients; the original input
|
||||
├── research.md # Architecture findings and the tracked risks
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Quickstart & Validation Guide: Midi Harbor
|
||||
|
||||
**Feature**: 001-service-and-clients | **Date**: 2026-09-20
|
||||
**Feature**: 001-service-and-clients, first written as 001-midi-connectivity-manager | **Date**: 2026-09-20
|
||||
|
||||
Runnable scenarios that prove the feature works end to end. Each maps to user stories and success
|
||||
criteria in the spec it validates. Scenarios are ordered so that each is useful on its own — you can
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Research: Service and Clients
|
||||
|
||||
**Feature**: 001-service-and-clients | **Date**: 2026-09-20
|
||||
**Feature**: 001-service-and-clients, first written as 001-midi-connectivity-manager | **Date**: 2026-09-20
|
||||
|
||||
All crate versions below were resolved against crates.io on 2026-09-20. Findings marked
|
||||
**VERIFIED** were proven empirically in this repository's environment (macOS 15.6, Apple Silicon,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# Feature Specification: Midi Harbor — Service and Clients
|
||||
|
||||
**Feature Branch**: `001-midi-connectivity-manager` (this spec's original name)
|
||||
|
||||
**Created**: 2026-09-20
|
||||
|
||||
**Status**: Implemented
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ any dependency is committed to.
|
|||
### Stage 0 spikes (retire RISK-2 and RISK-3 before committing dependencies)
|
||||
|
||||
- [x] T009 [P] Spike in `spikes/platform-midi/` proving direct `coremidi` 0.9 virtual endpoint creation with `kMIDIPropertyUniqueID` retrieval and `MIDINotifyProc` device notifications, and the `alsa` 0.12 sequencer equivalent — validates the R-003 decision to skip `midir`
|
||||
- [x] T010 Record spike outcomes in `specs/001-service-and-clients/research.md`, updating R-003, R-005, R-006 status from ASSUMED to VERIFIED or switching to the documented fallback; if the BLE peripheral spike fails, add the Complexity Tracking row in `plan.md` and the declared limitation in `.specify/memory/constitution.md`
|
||||
- [x] T010 Record spike outcomes in `specs/001-midi-connectivity-manager/research.md`, updating R-003, R-005, R-006 status from ASSUMED to VERIFIED or switching to the documented fallback; if the BLE peripheral spike fails, add the Complexity Tracking row in `plan.md` and the declared limitation in `.specify/memory/constitution.md`
|
||||
|
||||
**Checkpoint**: Dependencies are proven, not assumed. Fallbacks are chosen explicitly.
|
||||
|
||||
|
|
|
|||
|
|
@ -35,27 +35,6 @@ Linux-oriented and must be turned off on macOS.
|
|||
**Alternatives considered**: `iced` directly (libcosmic is built on it) — rejected because the user
|
||||
specified libcosmic and the spike proved it viable. `egui` — rejected for the same reason.
|
||||
|
||||
**Bumped to `03d7dcb832bc`** (libcosmic master of 2026-09-24, nine commits after `87ab8179`) on
|
||||
2026-09-29:
|
||||
|
||||
- The Windows failure the owner's notes (`veris/docs/libcosmic.md`) describe, `accesskit_winit`
|
||||
passing `&Window` where winit now has a trait, is still there at accesskit `6c20249`, but it does
|
||||
not reach this build: macOS and Windows turn libcosmic's default features off, `a11y` with them,
|
||||
so no accesskit crate is compiled there. Linux builds `accesskit_winit` with its Unix backend,
|
||||
which is unaffected. Enabling `a11y` on Windows would need the notes' `[patch]` of
|
||||
`wash2/accesskit` to a fork with the fix, rebased onto the accesskit revision libcosmic then
|
||||
uses.
|
||||
- `cargo update -p libcosmic` re-resolved `gpu-allocator`'s range for the `windows` crate to 0.57,
|
||||
then 0.61, which the newer accesskit and `ble-peripheral-rust` bring in. `wgpu-hal` 28.0.1 passes
|
||||
it `windows` 0.62 types, so the Windows build failed with mismatched `ID3D12Device` types. The
|
||||
lock file names `windows` 0.62.2 for `gpu-allocator`, as it did before the bump; check it after
|
||||
any `cargo update`, which may move it again.
|
||||
- The window at `03d7dcb832bc`: on macOS it opened with its menus; on Linux the AppImage's window
|
||||
rendered on X11 in XFCE; the Windows build passed its tests on the test machine, but the window
|
||||
was not seen there, the machine's desktop being in use.
|
||||
- libcosmic's X11 attributes are still overwritten by its Wayland ones
|
||||
(`iced/winit/src/conversion.rs`), so the X11 window class still comes from `argv[0]` (R-104).
|
||||
|
||||
---
|
||||
|
||||
## R-025: What building the window found that no test had
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Implementation Plan: Windows support
|
||||
|
||||
**Date**: 2026-09-26 | **Spec**: [spec.md](./spec.md)
|
||||
**Branch**: `002-windows-support`, this spec's original name | **Date**: 2026-09-26 | **Spec**: [spec.md](./spec.md)
|
||||
|
||||
**Input**: Feature specification from `/specs/013-windows-support/spec.md`
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Research: Windows support
|
||||
|
||||
**Feature**: 013-windows-support | **Date**: 2026-09-26
|
||||
**Feature**: 013-windows-support, first written as 002-windows-support | **Date**: 2026-09-26
|
||||
|
||||
Numbered after the original specification's research, whose last entry was R-082, so every R number in the
|
||||
project names one entry. Findings marked **VERIFIED** were measured on the Windows test machine:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# Feature Specification: Windows support
|
||||
|
||||
**Feature Branch**: `002-windows-support`, this spec's original name
|
||||
|
||||
**Created**: 2026-09-26
|
||||
|
||||
**Status**: Implemented
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Implementation Plan: Mac App Store mode
|
||||
|
||||
**Date**: 2026-09-27 | **Spec**: [spec.md](./spec.md)
|
||||
**Branch**: `003-mac-app-store-mode`, this spec's original name | **Date**: 2026-09-27 | **Spec**: [spec.md](./spec.md)
|
||||
|
||||
**Input**: Feature specification from `/specs/014-mac-app-store-mode/spec.md`
|
||||
|
||||
|
|
@ -95,7 +95,7 @@ design below: no change.
|
|||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/014-mac-app-store-mode/
|
||||
specs/014-mac-app-store-mode/ # first written as 003-mac-app-store-mode
|
||||
├── plan.md # This file
|
||||
├── research.md # R-094 to R-100
|
||||
├── data-model.md # Window state at quit, login item status
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Research: Mac App Store mode
|
||||
|
||||
**Feature**: 014-mac-app-store-mode | **Date**: 2026-09-27
|
||||
**Feature**: 014-mac-app-store-mode, first written as 003-mac-app-store-mode | **Date**: 2026-09-27
|
||||
|
||||
Numbered after the Windows port's research (013), whose last entry is R-093. Findings marked **VERIFIED**
|
||||
were measured on the development Mac (macOS 15, Darwin 24.6, Apple silicon) with the app
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
# Feature Specification: Mac App Store mode
|
||||
|
||||
**Feature Branch**: `003-mac-app-store-mode`, this spec's original name
|
||||
|
||||
**Created**: 2026-09-27
|
||||
|
||||
**Status**: Implemented
|
||||
|
|
|
|||
|
|
@ -1,118 +0,0 @@
|
|||
# Research: AppImage
|
||||
|
||||
The investigation behind this spec, under its number in the project-wide research log.
|
||||
|
||||
---
|
||||
|
||||
## R-104: Whether an AppImage can carry the daemon
|
||||
|
||||
**Status**: **VERIFIED** (2026-09-29) on Arch Linux (x86_64) and Ubuntu 22.04 (both
|
||||
architectures). Built as T246 and T247.
|
||||
|
||||
**The runtime.** AppImage's type 2 runtime (AppImage/type2-runtime, `runtime.c`, release
|
||||
20251108) mounts the image through FUSE at `/tmp/.mount_<first six letters of the file name><six
|
||||
random characters>`, sets `APPIMAGE` to the file and `APPDIR` to the mount, and replaces itself
|
||||
with `AppRun`. A forked child serves the mount while any process holds the read end of a pipe it
|
||||
leaves open across `exec`, and exits once none does. The runtime is static and needs only a setuid
|
||||
`fusermount` or `fusermount3`, not libfuse2, so Ubuntu 22.04 and later run it as installed.
|
||||
|
||||
**A daemon works under systemd.** The unit's `ExecStart` names the AppImage file, so every start,
|
||||
at login or after `Restart=always`, mounts it afresh. systemd tracks the process that became
|
||||
`AppRun`, and the mount's child runs in the same cgroup. The daemon replaces itself with `exec`
|
||||
only when CoreMIDI reports its server gone (R-079), which never happens on Linux; an `exec` would
|
||||
keep the mount in any case, since the process keeps the pipe.
|
||||
|
||||
**Stopping has to be ordered.** systemd's default `KillMode=control-group` sends SIGTERM to every
|
||||
process in the unit at once. The mount's child unmounts on SIGTERM while the daemon is still
|
||||
shutting down, and the daemon then touches a page of its executable that was never read: every
|
||||
`systemctl --user stop` and every logout ended in SIGBUS and a core dump, before the daemon had
|
||||
released its notes. `KillMode=mixed` signals only the daemon, but SIGKILLs the rest the moment it
|
||||
exits, before the child can unmount, which left one dead mount in `/tmp` per stop. The unit
|
||||
therefore has an `ExecStop` that sends SIGTERM to `$MAINPID` and waits for it to exit. systemd
|
||||
then signals what remains as usual, by which time the child has seen its pipe close and unmounted.
|
||||
From a package there is nothing else in the unit, so it stops as before.
|
||||
|
||||
After a crash, systemd's SIGTERM reaches only the child, which unmounts but leaves its empty
|
||||
directory in `/tmp`; a normal stop removes it. That is the runtime's behaviour and is cleared at
|
||||
reboot.
|
||||
|
||||
**The unit must not name the running executable.** `current_exe` gives the path inside the mount,
|
||||
which is gone once the daemon exits, so a unit naming it fails at the next login. Programs started
|
||||
from an AppImage inherit both variables, so a copy installed from a package and started from an
|
||||
AppImage terminal sees that terminal's `APPIMAGE`. The registered executable is therefore the
|
||||
`APPIMAGE` file only when the running executable is under `APPDIR`.
|
||||
|
||||
**Libraries.** The release binary links `libxkbcommon`, `libavahi-client`, `libavahi-common`,
|
||||
`libasound`, `libdbus-1`, `libm` and `libc`; the interface loads Wayland, X11, EGL and Vulkan while
|
||||
it runs. Only the two Avahi libraries are bundled, from the Debian 12 sysroot:
|
||||
|
||||
- AppImage's excludelist (AppImageCommunity/pkg2appimage) names `libasound.so.2`, since a bundled
|
||||
copy finds no sound cards, and the graphics libraries, which belong to the driver.
|
||||
- `libxkbcommon` stays with the host, whose `libxkbcommon-x11`, loaded by the interface, is built
|
||||
against the host's own.
|
||||
- The Avahi client talks to the Avahi daemon over D-Bus, and a desktop may have the daemon without
|
||||
the client library.
|
||||
|
||||
The bundled libraries are found through the binary's RUNPATH, `$ORIGIN/../lib`, set with patchelf.
|
||||
`LD_LIBRARY_PATH` in `AppRun` would reach every command the daemon runs, such as `systemctl`.
|
||||
|
||||
**The window on X11.** Tested in XFCE, the window was listed as "Untitled window" with the class
|
||||
`Midi-Harbor-0.1.0-x86_64.AppImage`, so no desktop paired it with its entry. Two causes, neither
|
||||
the AppImage's alone:
|
||||
|
||||
- The GUI never set a window title, so every Linux desktop showed none; the header draws its own,
|
||||
which is why it went unnoticed.
|
||||
- libcosmic at the pinned revision (`iced/winit/src/conversion.rs`) builds winit's X11 attributes
|
||||
with the application ID as the window's name, then replaces them with the Wayland attributes, so
|
||||
on X11 winit falls back to the file name of `argv[0]` for `WM_CLASS`
|
||||
(`winit-x11/src/window.rs`). From a package that is `midi-harbor`; from an AppImage it is the
|
||||
AppImage's file name, which the runtime passes as `argv[0]`. The desktop entry named
|
||||
`com.mrgeckosmedia.MidiHarbor`, which only Wayland's application ID ever matched.
|
||||
|
||||
The entry's `StartupWMClass` is now `midi-harbor`, and the AppImage's `AppRun` is a bash script
|
||||
that execs the binary with `exec -a midi-harbor`, so both builds carry that class. Wayland pairs by
|
||||
the application ID and the entry's file name, which are unchanged. `exec` keeps the process, so the
|
||||
unit's main process, the `ExecStop` and the runtime's mount behave as before.
|
||||
|
||||
**Updating the file.** Writing into the AppImage while the daemon runs fails with "Text file busy",
|
||||
the mount's child running from it. Moving a new file over it works, and the daemon runs the new one
|
||||
from its next start.
|
||||
|
||||
**Evidence**:
|
||||
|
||||
- Arch Linux, fuse3 only: `service install --start` wrote
|
||||
`ExecStart=/root/Apps/Midi-Harbor.AppImage daemon`; `/proc/<pid>/maps` showed both Avahi
|
||||
libraries from the mount and `libasound`, `libdbus-1` and `libxkbcommon` from `/usr/lib`; the
|
||||
ALSA sequencer's Midi Through port was listed; a network port was advertised on
|
||||
`_apple-midi._udp` and `network discover` found Windows peers.
|
||||
- After `kill -9`, systemd started the daemon again within four seconds on a new mount, and the old
|
||||
mount was gone.
|
||||
- With the unit as first written, `systemctl --user stop` and logging out both ended in
|
||||
`code=dumped, status=7/BUS`. With the `ExecStop`, three stops in a row each logged "daemon
|
||||
stopped", left no process and no mount, and a crash still restarted it.
|
||||
- Logging out, letting root's user manager stop, and logging in again over SSH started the unit
|
||||
with `default.target`, which is what a login does. With the `ExecStop`, the logout stopped the
|
||||
daemon cleanly and left one mount, the new daemon's.
|
||||
- Renamed to `Midi-Harbor-0.2.0-x86_64.AppImage`, `service status` reported the registration
|
||||
stale, naming the old path, and `service install` from the new name repaired it.
|
||||
- Ubuntu 22.04, glibc 2.35, without Avahi: the arm64 AppImage ran through FUSE in a container.
|
||||
The x86_64 one ran unpacked, since Docker's x86_64 emulation refuses the AppImage magic bytes
|
||||
in the ELF header's padding, and resolved both Avahi libraries from the bundle.
|
||||
- The window, on the Arch VM's Hyprland session as its desktop user, offered to register with
|
||||
systemd. A new AppImage's first launch took 22 seconds to show it, reading the image from the
|
||||
VM's disk; later launches took three to six seconds, even with a fresh home directory and the
|
||||
VM's page cache dropped, the host's cache still holding the image. The first two launches of the
|
||||
first build were checked at about eight seconds and taken for failures before this was known.
|
||||
|
||||
- XFCE on X11, as the desktop user: the window's "Install and start" registered
|
||||
`ExecStart=/home/<user>/Applications/Midi-Harbor-0.1.0-x86_64.AppImage daemon`, started it, and
|
||||
listed the ALSA Midi Through port. With the fixes the window is listed with the class
|
||||
`midi-harbor` and the title "Midi Harbor", and launched through its desktop entry, as AppImage
|
||||
integration tools install it, the taskbar shows the entry's icon.
|
||||
- A new build moved over the registered file while the daemon ran, then `systemctl --user restart`:
|
||||
the old daemon logged "daemon stopped", the new one started, and no core dump.
|
||||
|
||||
- A reboot with the service installed from the window: the daemon logged "daemon stopped" as the
|
||||
machine shut down, and started from the AppImage 35 seconds later, when SDDM logged the user in.
|
||||
|
||||
**Not checked**: an arm64 machine outside Docker.
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
# Feature Specification: AppImage
|
||||
|
||||
**Created**: 2026-09-29
|
||||
|
||||
**Status**: Implemented; built for x86_64 and arm64 and checked on 2026-09-29 on Arch Linux and
|
||||
Ubuntu 22.04
|
||||
|
||||
**Input**: User request: "I'd like to see what it'll take to make an app image of this project. I
|
||||
don't know if its something that'll allow a daemon process, so please research and see.", then
|
||||
"If you think its feasible, do it."
|
||||
|
||||
The `.deb` and `.rpm` packages cover Debian, Ubuntu, Fedora and RHEL. Every other distribution had
|
||||
only the `.tar.gz` of the bare binary, with no desktop entry and no libraries. A release now
|
||||
carries an AppImage for each Linux architecture as well, which runs the graphical interface and
|
||||
the daemon it registers with systemd.
|
||||
|
||||
## User Scenarios & Testing *(mandatory)*
|
||||
|
||||
### User Story 1 - Running Midi Harbor from an AppImage (Priority: P1)
|
||||
|
||||
A user on a distribution without a package downloads the AppImage, makes it executable and opens
|
||||
it. The window offers to start the daemon at login, and from then on the daemon starts at every
|
||||
login and after every crash, as it does when installed from a package.
|
||||
|
||||
**Why this priority**: it is the whole request.
|
||||
|
||||
**Independent Test**: On a Linux machine with a systemd user session, run
|
||||
`service install --start` from the AppImage, then check the unit, the running daemon, a restart
|
||||
after the daemon is killed, and an advertised network port.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** the AppImage, **When** `service install` runs from it, **Then** the unit starts the
|
||||
AppImage file, not the executable inside its mount.
|
||||
2. **Given** the service installed from the AppImage, **When** the daemon is killed, **Then**
|
||||
systemd starts it again from the AppImage.
|
||||
3. **Given** the daemon running from the AppImage, **When** the service is stopped or the user
|
||||
logs out, **Then** the daemon shuts down as it does from a package, releasing its notes, and
|
||||
leaves no mount behind.
|
||||
4. **Given** a distribution without the Avahi client libraries, **When** a network port is
|
||||
created, **Then** it is advertised, the AppImage carrying the libraries.
|
||||
5. **Given** a newer AppImage moved over the registered one, **When** the daemon next starts,
|
||||
**Then** it runs the newer one.
|
||||
6. **Given** a newer AppImage saved under another name and the old one deleted, **When**
|
||||
`service status` runs, **Then** it reports the registration stale, and `service install` from
|
||||
the new file repairs it.
|
||||
|
||||
### User Story 2 - Building the AppImages (Priority: P1)
|
||||
|
||||
`make snapshot` and `make release` build an AppImage for x86_64 and arm64 beside the other
|
||||
artifacts, and the release publishes them.
|
||||
|
||||
**Independent Test**: Run the build and check both AppImages exist and start.
|
||||
|
||||
## Requirements *(mandatory)*
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- **FR-I01**: `service install` run from an AppImage MUST register the AppImage file, and run from
|
||||
anything else MUST register the running executable, whatever AppImage variables it inherited.
|
||||
- **FR-I04**: Stopping the service MUST let the daemon finish its shutdown before anything else in
|
||||
the unit is stopped.
|
||||
- **FR-I02**: The release MUST build an AppImage for each Linux architecture it builds, from the
|
||||
same binary as the packages.
|
||||
- **FR-I03**: The AppImage MUST run on the distributions the packages do, needing from the host only
|
||||
glibc, FUSE and what a desktop already has.
|
||||
|
||||
## Success Criteria *(mandatory)*
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-I01**: From a downloaded AppImage, the daemon runs at login after one setup step, the same
|
||||
step as from a package.
|
||||
|
||||
## Assumptions
|
||||
|
||||
- The AppImage is for desktops. A server takes the headless archive or package, so there is no
|
||||
headless AppImage.
|
||||
- Updates are by hand. AppImageUpdate's update information and `.zsync` file can be added later
|
||||
without changing the daemon.
|
||||
- AppImage's portable mode, a `.home` or `.config` directory beside the file, is not supported:
|
||||
it moves where the unit is written, where systemd does not look.
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
# Tasks: AppImage
|
||||
|
||||
Tasks by their numbers in the project-wide sequence, which continues across every spec.
|
||||
|
||||
- [x] T246 Register the AppImage file rather than the executable inside its mount, per FR-I01 and FR-I04 — done: `ServiceSpec::for_current_executable` takes `APPIMAGE` when the running executable is under `APPDIR`, so another AppImage's inherited variables are ignored; and the unit's `ExecStop` stops the daemon alone and waits for it, since stopping the whole unit at once unmounted the AppImage under the daemon and killed it with SIGBUS (R-104). Unit tested in `crates/service/src/lib.rs` against the variables the AppImage runtime sets, and in `crates/service/src/systemd.rs` for the `ExecStop`.
|
||||
- [x] T247 Build and publish an AppImage for each Linux architecture, per FR-I02 and FR-I03 — done: `packaging/linux/appimage.sh`, run by GoReleaser after each full Linux build, bundles the Avahi client libraries and their license from the sysroot, sets the binary's RUNPATH, and packs it with appimagetool and the pinned runtime, which the cross image now carries; the release and checksums include `dist/*.AppImage`. Checked on Arch Linux and Ubuntu 22.04 (R-104); not unit tested, since it is a build script.
|
||||
- [x] T248 Name the window on Linux so taskbars pair it with its desktop entry — done: the window's title is set to "Midi Harbor" at start, the desktop entry's `StartupWMClass` is `midi-harbor`, and the AppImage's `AppRun` runs the binary as `midi-harbor`, since the pinned libcosmic leaves the X11 class to `argv[0]` (R-104). Checked in XFCE on X11; not unit tested, since it is window-system wiring.
|
||||
|
|
@ -27,7 +27,6 @@ exception is 014, whose task list started again at T001: its T001 to T034 are ci
|
|||
| [014-mac-app-store-mode](014-mac-app-store-mode/spec.md) | The sandboxed App Store build, run from the menu bar |
|
||||
| [015-mac-menus](015-mac-menus/spec.md) | The menus on macOS: File, Edit, View, Window and Help |
|
||||
| [016-app-store-submission](016-app-store-submission/spec.md) | Building the package App Store Connect takes |
|
||||
| [017-appimage](017-appimage/spec.md) | The AppImage for Linux, and registering the daemon from it |
|
||||
|
||||
## Where each number is
|
||||
|
||||
|
|
@ -138,9 +137,12 @@ exception is 014, whose task list started again at T001: its T001 to T034 are ci
|
|||
- **Research**: R-103
|
||||
- **Tasks**: T245
|
||||
|
||||
### 017-appimage
|
||||
## Earlier names
|
||||
|
||||
- **Requirements**: FR-I01, FR-I02, FR-I03, FR-I04
|
||||
- **Success criteria**: SC-I01
|
||||
- **Research**: R-104
|
||||
- **Tasks**: T246–T248
|
||||
Until 2026-09-27 there were three specs. Branches, commit messages and older notes use these names.
|
||||
|
||||
| Earlier | Now |
|
||||
|---|---|
|
||||
| `001-midi-connectivity-manager` | Split into 001 to 010: the service and command line stayed in [001](001-service-and-clients/spec.md), each capability moved to its own spec, and the tasks added since for the MIDI service crash and the test note became 011 and 012 |
|
||||
| `002-windows-support` | [013-windows-support](013-windows-support/spec.md) |
|
||||
| `003-mac-app-store-mode` | [014-mac-app-store-mode](014-mac-app-store-mode/spec.md) |
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue