Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
168314e
feat: core backend - actor profiles, fades, Yamaha SCP, show control
johnqherman Jun 30, 2026
4969cfd
feat: Allen & Heath input-channel fader control via NRPN
johnqherman Jun 30, 2026
c05e5a6
feat: positions, Cue Zero, MTC timecode, channel monitoring
johnqherman Jun 30, 2026
ef9f984
chore: merge backend additions
johnqherman Jun 30, 2026
5102a11
feat: UI surfacing + service wiring
johnqherman Jun 30, 2026
96e6347
fix: X32 meter feed, exact WING fader law, build warning
johnqherman Jun 30, 2026
4d08ed8
fix: verify Allen & Heath SQ NRPN encoding against official spec
johnqherman Jun 30, 2026
dafed0b
chore: terse comments, drop external references
johnqherman Jun 30, 2026
d7f98fc
feat: console scribble strips and ensembles
johnqherman Jul 1, 2026
c6d0a4b
feat: cue fx mutes, snippets, L/R gang, color/skip, soundcheck mode
johnqherman Jul 1, 2026
aea662d
chore: merge cue-content features
johnqherman Jul 1, 2026
d64a91f
chore: use American spelling (color)
johnqherman Jul 1, 2026
040126f
feat: console-workflow refinements + Settings dialog (highlight, mute…
johnqherman Jul 1, 2026
c490060
feat: UI panels for positions, cue zero, and timecode triggers
johnqherman Jul 1, 2026
15cb176
feat: cue clone, copy/paste/merge/swap, fill down, jump, and edit lock
johnqherman Jul 1, 2026
8ab5a3f
feat: import .tmix show files
johnqherman Jul 1, 2026
8b4c20d
feat: edit history browser and cue-list CSV export
johnqherman Jul 1, 2026
5868816
feat: channel utilisation report
johnqherman Jul 1, 2026
b3d7466
feat: cue-table row size selector
johnqherman Jul 1, 2026
f369a02
feat: cue-table column visibility toggles
johnqherman Jul 1, 2026
ad04f18
feat: spare-backup mic allocation model
johnqherman Jul 1, 2026
46e8d36
feat: spare-backup switch dialog and mixer apply
johnqherman Jul 1, 2026
9f07b5c
feat: export cue notes to text file
johnqherman Jul 1, 2026
f3a76e6
feat: active cue info panel
johnqherman Jul 1, 2026
e6e491a
feat: clone level offsets between cues
johnqherman Jul 1, 2026
19c0fad
feat: FX unit registry and channel assignments
johnqherman Jul 1, 2026
fd39870
feat: FX setup dialog
johnqherman Jul 1, 2026
7345385
fix: scroll cue editor sections and declutter timecode add row
johnqherman Jul 1, 2026
0d61910
feat: startup welcome screen with recent shows
johnqherman Jul 1, 2026
1805805
chore: use repository description for app text and generic import labels
johnqherman Jul 1, 2026
0eaa8a4
feat: cpack packaging with desktop entry and app icon
johnqherman Jul 1, 2026
5b268b5
feat: quick start and feature guide help
johnqherman Jul 1, 2026
23980c2
chore: US spelling app-wide (utilization, center, behavior)
johnqherman Jul 1, 2026
5fd0a48
feat: record live console fader offsets into a cue
johnqherman Jul 1, 2026
5e3a53c
fix: replace welcome-screen blurb with a functional prompt
johnqherman Jul 1, 2026
749b315
refactor: remove unused programmer-mode gating
johnqherman Jul 1, 2026
747d51b
feat: QLab panic/stop/pause/resume transport verbs
johnqherman Jul 1, 2026
08dada3
feat: discard notes action
johnqherman Jul 1, 2026
7100ad4
feat: inline DCA, position, and FX cue-table columns
johnqherman Jul 1, 2026
d28c60b
feat: REAPER virtual-sound-check marker sync
johnqherman Jul 1, 2026
9a65e7a
feat: marker notes with HTML export
johnqherman Jul 1, 2026
38b34f5
feat: REAPER transport auto-detect via OSC feedback
johnqherman Jul 1, 2026
9002e4c
feat: console snippet/scene name cache
johnqherman Jul 1, 2026
2974b2d
feat: populate console name cache from X32 and show snippet names
johnqherman Jul 1, 2026
249b7c3
feat: record faders mode writes live moves into the current cue
johnqherman Jul 1, 2026
04c6935
feat: OSC remote stopall and pauseresumeall verbs
johnqherman Jul 1, 2026
fd83e8f
feat: Cue Player (cpsound) outbound OSC integration
johnqherman Jul 1, 2026
663832e
feat: DiGiCo SD-series console driver
johnqherman Jul 1, 2026
3603efa
feat: Show Cue System (SCS) outbound OSC integration
johnqherman Jul 1, 2026
aecbd08
feat: standby cue highlight, filter pinning, unified status colors
johnqherman Jul 1, 2026
c37ecc1
feat: type-to-jump box, cue context menu, type icons, fade column
johnqherman Jul 1, 2026
beb064a
feat: bigger live controls, grouped console list, persisted columns, …
johnqherman Jul 1, 2026
71437e7
feat: tabbed remote dialog with field gating, high-contrast booth theme
johnqherman Jul 1, 2026
9d03b70
feat: collapsible editor sections (collapsed by default), fix standby…
johnqherman Jul 1, 2026
088ee3a
feat: chevron collapsible sections (no frames), pad live-control buttons
johnqherman Jul 1, 2026
e475ad4
fix: reduce live-control button padding
johnqherman Jul 1, 2026
f0b4205
fix: icon-only GO/Stop transport buttons
johnqherman Jul 1, 2026
97ced62
feat: icon-only PANIC, distinct bubble-bar icons, wrap bubble bar to …
johnqherman Jul 1, 2026
1406ccf
fix: tighter tooltip padding and faster appearance
johnqherman Jul 1, 2026
34ca832
fix: fit bubble-bar icons on a single row
johnqherman Jul 1, 2026
7f3f9a6
feat: center bubble bar and tooltips horizontally
johnqherman Jul 1, 2026
97ff463
fix: center tooltips under widget incl action buttons, icon-only prev…
johnqherman Jul 1, 2026
053059b
fix: center tooltips on cursor position
johnqherman Jul 1, 2026
11b9724
fix: recenter tooltip on cursor via real width, shorten tooltip text
johnqherman Jul 1, 2026
444782a
revert: use default tooltip positioning
johnqherman Jul 1, 2026
5f5a286
fix: crash deleting the playing cue (model remove ordering + stale pl…
johnqherman Jul 1, 2026
4bf19a7
fix: multiple crash-class bugs (move/insert model ordering, GoTo/macr…
johnqherman Jul 1, 2026
ea3c309
fix: nested-macro state clobber (local state) and FadeEngine advance …
johnqherman Jul 1, 2026
00bf545
feat: empty-state hint for the cue list
johnqherman Jul 2, 2026
12fe002
fix: macro cue type data-loss, empty-state hint, tooltip/mnemonic/uni…
johnqherman Jul 2, 2026
bf67138
feat: TheatreMix-style layout - cue grid over channel strip, editor a…
johnqherman Jul 2, 2026
8c19b06
feat: match TheatreMix menus (Cue List menu), column names (Cue/Text)…
johnqherman Jul 2, 2026
611c229
feat: TheatreMix-style cue grid columns (dot, Cue, Text, FX, Snip, QL…
johnqherman Jul 2, 2026
89bcedf
feat: per-DCA triplet columns [DCA n | fx | pos] with FX/Pos column t…
johnqherman Jul 2, 2026
267d05b
feat: consolidate to TheatreMix 5-menu bar (File/Edit/View/Cue List/H…
johnqherman Jul 2, 2026
9bde199
feat: per-channel status strip (tiles colored by monitor state), mixe…
johnqherman Jul 2, 2026
0c1e60c
feat: add scenes + per-channel FX to cue model with persistence and S…
johnqherman Jul 2, 2026
a312a9b
feat: populate per-DCA fx/pos columns from channelFX/positions via DC…
johnqherman Jul 2, 2026
634f40e
feat: in-grid Assign prompts on double-click for DCA/Scene/Snip/QLab …
johnqherman Jul 2, 2026
19364c5
style: remove em-dashes from user-facing strings
johnqherman Jul 2, 2026
a42046b
feat: CPack installers in CI releases (deb/rpm/NSIS/dmg) + in-app upd…
johnqherman Jul 2, 2026
03d0d0f
feat: AutoUpdater with WinSparkle/Sparkle silent updates, Linux notif…
johnqherman Jul 2, 2026
296e863
feat: CI appcast publishing and macOS Sparkle bundle plist
johnqherman Jul 2, 2026
3a983df
feat: host auto-update appcasts and installers on openmix.dev VPS
johnqherman Jul 2, 2026
4c9fd15
feat: prune VPS download dirs to newest 5 releases
johnqherman Jul 2, 2026
094c9a2
chore: set Sparkle EdDSA public key for update verification
johnqherman Jul 2, 2026
5679382
fix: link liblo via platform-agnostic interface target for Windows bu…
johnqherman Jul 2, 2026
09ae6ef
fix: resolve WinSparkle x64 lib path and cap Linux build parallelism
johnqherman Jul 2, 2026
3511014
fix: cap Linux build parallelism and glob WinSparkle header path
johnqherman Jul 2, 2026
1c8c84c
fix: cap macOS build parallelism to avoid runner OOM
johnqherman Jul 2, 2026
4aa1ae3
fix: skip PCH for Objective-C++ Sparkle source on macOS
johnqherman Jul 2, 2026
a1a6b15
fix: add /download landing page and drop duplicate appcast length attr
johnqherman Jul 2, 2026
878bfd0
fix(ui): auto-fit cue list columns instead of stretching last
johnqherman Jul 2, 2026
5f0c42a
fix(build): app icons, liblo DLL, installer shortcuts
johnqherman Jul 2, 2026
75e9ee7
Merge branch 'master' into show-control-features
johnqherman Jul 2, 2026
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
70 changes: 54 additions & 16 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ jobs:
run: cmake -B build -DCMAKE_BUILD_TYPE=Release

- name: Build
run: cmake --build build --config Release --parallel
# cap parallelism: full -j OOM-kills cc1plus (heavy Qt PCH) on the runner
run: cmake --build build --config Release --parallel 2

- name: Test
run: ctest --test-dir build --output-on-failure
Expand Down Expand Up @@ -136,7 +137,9 @@ jobs:
-DCMAKE_PREFIX_PATH=/opt/homebrew/opt/qt@6

- name: Build
run: cmake --build build --config Release --parallel
# cap parallelism: full -j exhausts the 7GB arm64 runner on Qt PCH
# (cc1plus), starving/killing the runner ("lost communication")
run: cmake --build build --config Release --parallel 2

- name: Test
run: ctest --test-dir build --output-on-failure
Expand Down Expand Up @@ -172,9 +175,10 @@ jobs:
OpenMix-windows/*
OpenMix-macos/*

# generate the Sparkle/WinSparkle appcasts that the app polls, and publish
# them to GitHub Pages. Requires a Sparkle EdDSA private key in the
# OPENMIX_SPARKLE_ED_PRIVATE_KEY secret (see docs/AUTOUPDATE.md).
# generate the Sparkle/WinSparkle appcasts + installer tree and rsync them to
# the openmix.dev VPS. Requires a Sparkle EdDSA private key in
# OPENMIX_SPARKLE_ED_PRIVATE_KEY and OPENMIX_DEPLOY_* SSH secrets
# (see docs/AUTOUPDATE.md).
appcast:
if: startsWith(github.ref, 'refs/tags/v')
needs: [release]
Expand All @@ -190,22 +194,22 @@ jobs:
- name: Download artifacts
uses: actions/download-artifact@v4

- name: Build appcasts
- name: Build appcasts + installer tree
env:
SPARKLE_KEY: ${{ secrets.OPENMIX_SPARKLE_ED_PRIVATE_KEY }}
TAG: ${{ github.ref_name }}
run: |
VERSION="${TAG#v}"
BASE="https://github.com/johnqherman/OpenMix/releases/download/${TAG}"
mkdir -p site
BASE="https://openmix.dev/download/${TAG}"
mkdir -p site/download/${TAG}
cp OpenMix-macos/* OpenMix-windows/* OpenMix-linux/* site/download/${TAG}/ 2>/dev/null || true

# macOS: sign the DMG with Sparkle's EdDSA key and emit a full appcast
SIGN=$(find /opt/homebrew /usr/local -name sign_update -type f 2>/dev/null | head -1)
DMG=$(ls OpenMix-macos/*.dmg | head -1)
if [ -n "$SPARKLE_KEY" ] && [ -n "$SIGN" ] && [ -n "$DMG" ]; then
echo "$SPARKLE_KEY" > /tmp/ed_key
SIG=$("$SIGN" -f /tmp/ed_key "$DMG") # prints sparkle:edSignature=... length=...
LEN=$(stat -f%z "$DMG")
cat > site/appcast-macos.xml <<EOF
<?xml version="1.0"?>
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
Expand All @@ -214,7 +218,7 @@ jobs:
<title>OpenMix ${VERSION}</title>
<sparkle:version>${VERSION}</sparkle:version>
<sparkle:shortVersionString>${VERSION}</sparkle:shortVersionString>
<enclosure url="${BASE}/$(basename "$DMG")" ${SIG} sparkle:version="${VERSION}" length="${LEN}" type="application/octet-stream"/>
<enclosure url="${BASE}/$(basename "$DMG")" ${SIG} sparkle:version="${VERSION}" type="application/octet-stream"/>
</item>
</channel>
</rss>
Expand All @@ -240,9 +244,43 @@ jobs:
EOF
fi

- name: Publish to GitHub Pages
uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./site
keep_files: true
# Linux notify-fallback: tiny JSON polled by UpdateChecker
cat > site/latest.json <<EOF
{"tag_name": "${TAG}", "html_url": "https://openmix.dev/download"}
EOF

# download landing page: nginx autoindex is off, so /download/
# 403s without an index.html. list the current release's files.
{
echo '<!doctype html><html lang="en"><head><meta charset="utf-8">'
echo '<meta name="viewport" content="width=device-width,initial-scale=1">'
echo "<title>Download OpenMix ${VERSION}</title>"
echo '<style>body{font-family:system-ui,sans-serif;max-width:40rem;margin:3rem auto;padding:0 1rem;line-height:1.7}a{color:#2563eb}</style>'
echo "</head><body><h1>Download OpenMix ${VERSION}</h1><ul>"
for f in site/download/${TAG}/*; do
[ -f "$f" ] || continue
n=$(basename "$f")
echo "<li><a href=\"${TAG}/${n}\">${n}</a></li>"
done
echo '</ul></body></html>'
} > site/download/index.html

- name: Deploy to VPS
env:
SSH_KEY: ${{ secrets.OPENMIX_DEPLOY_SSH_KEY }}
SSH_HOST: ${{ secrets.OPENMIX_DEPLOY_HOST }}
SSH_USER: ${{ secrets.OPENMIX_DEPLOY_USER }}
DEPLOY_PATH: ${{ secrets.OPENMIX_DEPLOY_PATH }}
run: |
if [ -z "$SSH_KEY" ] || [ -z "$SSH_HOST" ]; then
echo "::error::Missing OPENMIX_DEPLOY_* secrets; cannot publish to VPS"
exit 1
fi
mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/deploy && chmod 600 ~/.ssh/deploy
ssh-keyscan -H "$SSH_HOST" >> ~/.ssh/known_hosts 2>/dev/null
# keep old release dirs (keep_files-style); appcasts + latest.json overwrite
rsync -avz -e "ssh -i ~/.ssh/deploy" site/ "${SSH_USER}@${SSH_HOST}:${DEPLOY_PATH}/"
# prune download/ to the newest 5 release dirs (version-sorted)
ssh -i ~/.ssh/deploy "${SSH_USER}@${SSH_HOST}" \
"cd '${DEPLOY_PATH}/download' && ls -1d v*/ 2>/dev/null | sed 's:/*\$::' | sort -V | head -n -5 | xargs -r rm -rf --"
76 changes: 64 additions & 12 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,26 @@ FetchContent_Declare(
)
FetchContent_MakeAvailable(qlementine-icons)

# find liblo for OSC
# find liblo for OSC; expose one platform-agnostic target (openmix_liblo) so app
# and tests link the same name on every OS
add_library(openmix_liblo INTERFACE)
if(WIN32)
find_path(LIBLO_INCLUDE_DIRS lo/lo.h REQUIRED)
find_library(LIBLO_LIBRARIES NAMES lo liblo REQUIRED)
target_include_directories(openmix_liblo INTERFACE ${LIBLO_INCLUDE_DIRS})
target_link_libraries(openmix_liblo INTERFACE ${LIBLO_LIBRARIES})
# the deploy script only bundles Qt; ship liblo's runtime DLL ourselves
get_filename_component(_liblo_libdir ${LIBLO_LIBRARIES} DIRECTORY)
find_file(LIBLO_DLL NAMES lo.dll liblo.dll
HINTS ${_liblo_libdir}/../bin ${LIBLO_INCLUDE_DIRS}/../bin)
if(NOT LIBLO_DLL)
message(FATAL_ERROR "liblo runtime DLL not found; the installer would ship without it")
endif()
install(FILES ${LIBLO_DLL} DESTINATION ${CMAKE_INSTALL_BINDIR})
else()
find_package(PkgConfig REQUIRED)
pkg_check_modules(LIBLO REQUIRED IMPORTED_TARGET liblo)
target_link_libraries(openmix_liblo INTERFACE PkgConfig::LIBLO)
endif()

# RtMidi
Expand Down Expand Up @@ -351,12 +364,7 @@ target_link_libraries(${PROJECT_NAME} PRIVATE
)

# liblo
if(WIN32)
target_include_directories(${PROJECT_NAME} PRIVATE ${LIBLO_INCLUDE_DIRS})
target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBLO_LIBRARIES})
else()
target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::LIBLO)
endif()
target_link_libraries(${PROJECT_NAME} PRIVATE openmix_liblo)

target_link_libraries(${PROJECT_NAME} PRIVATE RtMidi)

Expand All @@ -367,18 +375,42 @@ if(WIN32)
FetchContent_Declare(winsparkle
URL https://github.com/vslavik/winsparkle/releases/download/v0.8.1/WinSparkle-0.8.1.zip)
FetchContent_MakeAvailable(winsparkle)
set(WINSPARKLE_ROOT ${winsparkle_SOURCE_DIR}/WinSparkle-0.8.1)
target_include_directories(${PROJECT_NAME} PRIVATE ${WINSPARKLE_ROOT}/include)
target_link_libraries(${PROJECT_NAME} PRIVATE ${WINSPARKLE_ROOT}/x64/WinSparkle.lib)
# the 0.8.1 zip layout (nesting, x64/Release/ subdir) is fragile; glob for the
# header, x64 lib, and x64 dll so the exact structure can't break the build
file(GLOB_RECURSE _ws_hdrs "${winsparkle_SOURCE_DIR}/*winsparkle.h")
file(GLOB_RECURSE _ws_libs "${winsparkle_SOURCE_DIR}/*WinSparkle.lib")
file(GLOB_RECURSE _ws_dlls "${winsparkle_SOURCE_DIR}/*WinSparkle.dll")
list(GET _ws_hdrs 0 _ws_hdr)
get_filename_component(WINSPARKLE_INCLUDE ${_ws_hdr} DIRECTORY)
foreach(f ${_ws_libs})
if(f MATCHES "x64")
set(WINSPARKLE_LIB ${f})
endif()
endforeach()
foreach(f ${_ws_dlls})
if(f MATCHES "x64")
set(WINSPARKLE_DLL ${f})
endif()
endforeach()
if(NOT WINSPARKLE_INCLUDE OR NOT WINSPARKLE_LIB OR NOT WINSPARKLE_DLL)
message(FATAL_ERROR "WinSparkle header/x64 lib/dll not found under ${winsparkle_SOURCE_DIR}")
endif()
target_include_directories(${PROJECT_NAME} PRIVATE ${WINSPARKLE_INCLUDE})
target_link_libraries(${PROJECT_NAME} PRIVATE ${WINSPARKLE_LIB})
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${WINSPARKLE_ROOT}/x64/WinSparkle.dll $<TARGET_FILE_DIR:${PROJECT_NAME}>)
install(FILES ${WINSPARKLE_ROOT}/x64/WinSparkle.dll DESTINATION ${CMAKE_INSTALL_BINDIR})
${WINSPARKLE_DLL} $<TARGET_FILE_DIR:${PROJECT_NAME}>)
install(FILES ${WINSPARKLE_DLL} DESTINATION ${CMAKE_INSTALL_BINDIR})
elseif(APPLE)
FetchContent_Declare(sparkle
URL https://github.com/sparkle-project/Sparkle/releases/download/2.6.4/Sparkle-2.6.4.tar.xz)
FetchContent_MakeAvailable(sparkle)
target_sources(${PROJECT_NAME} PRIVATE src/app/AutoUpdaterSparkle.mm)
# the C++ PCH is built with Objective-C disabled; reusing it for this
# Objective-C++ source fails ("Objective-C was disabled in precompiled
# file ... but is currently enabled"), so skip the PCH for it
set_source_files_properties(src/app/AutoUpdaterSparkle.mm
PROPERTIES SKIP_PRECOMPILE_HEADERS ON)
target_compile_options(${PROJECT_NAME} PRIVATE -F${sparkle_SOURCE_DIR})
target_link_libraries(${PROJECT_NAME} PRIVATE "-F${sparkle_SOURCE_DIR}" "-framework Sparkle")
configure_file(${CMAKE_SOURCE_DIR}/packaging/Info.plist.in
Expand Down Expand Up @@ -415,13 +447,21 @@ if(WIN32)
set_target_properties(${PROJECT_NAME} PROPERTIES
WIN32_EXECUTABLE TRUE
)
# embed the app icon into the executable (needs the RC language, which
# project(LANGUAGES CXX) leaves off)
enable_language(RC)
target_sources(${PROJECT_NAME} PRIVATE packaging/openmix.rc)
elseif(APPLE)
set_target_properties(${PROJECT_NAME} PROPERTIES
MACOSX_BUNDLE TRUE
MACOSX_BUNDLE_BUNDLE_NAME "OpenMix"
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION}
)
# bundle icon: ship the icns in Resources (Info.plist.in names it)
target_sources(${PROJECT_NAME} PRIVATE packaging/openmix.icns)
set_source_files_properties(packaging/openmix.icns
PROPERTIES MACOSX_PACKAGE_LOCATION Resources)
endif()

# install rules
Expand Down Expand Up @@ -464,6 +504,18 @@ if(WIN32)
set(CPACK_NSIS_DISPLAY_NAME "OpenMix")
set(CPACK_NSIS_ENABLE_UNINSTALL_BEFORE_INSTALL ON)
set(CPACK_NSIS_MODIFY_PATH OFF)
# installer/uninstaller + Add/Remove Programs icons
set(CPACK_NSIS_MUI_ICON "${CMAKE_SOURCE_DIR}/packaging/openmix.ico")
set(CPACK_NSIS_MUI_UNIICON "${CMAKE_SOURCE_DIR}/packaging/openmix.ico")
set(CPACK_NSIS_INSTALLED_ICON_NAME "bin\\\\OpenMix.exe")
# start menu entry + desktop shortcut (removed again on uninstall)
set(CPACK_PACKAGE_EXECUTABLES "OpenMix" "OpenMix")
set(CPACK_NSIS_CREATE_ICONS_EXTRA
"CreateShortCut '$DESKTOP\\\\OpenMix.lnk' '$INSTDIR\\\\bin\\\\OpenMix.exe'")
set(CPACK_NSIS_DELETE_ICONS_EXTRA
"Delete '$DESKTOP\\\\OpenMix.lnk'")
# offer to launch the app from the installer's finish page
set(CPACK_NSIS_MUI_FINISHPAGE_RUN "bin\\\\OpenMix.exe")
elseif(APPLE)
set(CPACK_GENERATOR "DragNDrop")
else()
Expand Down
34 changes: 24 additions & 10 deletions docs/AUTOUPDATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,39 @@ pipeline hinges on one signing key and a place to host the appcasts.
the `sparkle:edSignature` in the macOS appcast. Without it, the appcast job
logs a warning and macOS clients will not accept the update.

## Appcast hosting (GitHub Pages)
## Appcast + installer hosting (openmix.dev VPS)

The apps poll these URLs (set in `Info.plist.in` `SUFeedURL` and in
`AutoUpdater.cpp` for WinSparkle):
Everything is served from the VPS. The apps poll:

- macOS: `https://johnqherman.github.io/OpenMix/appcast-macos.xml`
- Windows: `https://johnqherman.github.io/OpenMix/appcast-windows.xml`
- macOS: `https://openmix.dev/appcast-macos.xml` (`Info.plist.in` `SUFeedURL`)
- Windows: `https://openmix.dev/appcast-windows.xml` (`AutoUpdater.cpp`)
- Linux notify-fallback: `https://openmix.dev/latest.json` (`UpdateChecker.cpp`)

The `appcast` job in `.github/workflows/build.yml` builds both files on every
`v*` tag and publishes them to the `gh-pages` branch (via
`peaceiris/actions-gh-pages`, `keep_files: true` so old entries survive). Enable
GitHub Pages for the repo, serving from the `gh-pages` branch root.
Installers live under `https://openmix.dev/download/<tag>/`. The `appcast` job in
`.github/workflows/build.yml` builds the appcasts + `latest.json`, lays out the
installer tree, and `rsync`s the whole `site/` over SSH to the VPS webroot on
every `v*` tag. After upload it prunes `download/` to the newest 5 release dirs
(version-sorted); the appcasts and `latest.json` at the root are overwritten each
release.

### Deploy secrets (GitHub Actions)

| Secret | Value |
|--------|-------|
| `OPENMIX_DEPLOY_SSH_KEY` | private SSH key whose public half is in the deploy user's `authorized_keys` |
| `OPENMIX_DEPLOY_HOST` | `openmix.dev` (or the VPS IP) |
| `OPENMIX_DEPLOY_USER` | deploy user (e.g. `deploy`) |
| `OPENMIX_DEPLOY_PATH` | webroot rsync target (e.g. `/var/www/openmix.dev`) |

A GitHub Release is still created as a mirror, but the app updater points only at
openmix.dev.

## Release flow

1. Bump `VERSION` / `project(... VERSION x.y.z)` in `CMakeLists.txt`.
2. Tag and push: `git tag vx.y.z && git push origin vx.y.z`.
3. CI: `build-*` jobs make installers, `release` attaches them to the GitHub
Release, `appcast` signs + publishes the appcasts to `gh-pages`.
Release, `appcast` signs + rsyncs installers and appcasts to openmix.dev.
4. Installed clients pick up the update on their next scheduled check.

## Code signing (required for a clean silent update)
Expand Down
6 changes: 4 additions & 2 deletions packaging/Info.plist.in
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,23 @@
<string>@PROJECT_VERSION@</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleIconFile</key>
<string>openmix</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>LSMinimumSystemVersion</key>
<string>11.0</string>

<!-- Sparkle auto-update -->
<key>SUFeedURL</key>
<string>https://johnqherman.github.io/OpenMix/appcast-macos.xml</string>
<string>https://openmix.dev/appcast-macos.xml</string>
<key>SUEnableAutomaticChecks</key>
<true/>
<key>SUAutomaticallyUpdate</key>
<true/>
<!-- EdDSA public key; must match the OPENMIX_SPARKLE_ED_PRIVATE_KEY signing
secret used by CI (see docs/AUTOUPDATE.md). -->
<key>SUPublicEDKey</key>
<string>REPLACE_WITH_SPARKLE_ED_PUBLIC_KEY</string>
<string>Th9esMBuKy6nRfgTeOA7eeUr5Ms3fBaGtLZFI5hyH7c=</string>
</dict>
</plist>
Binary file added packaging/openmix.icns
Binary file not shown.
Binary file added packaging/openmix.ico
Binary file not shown.
1 change: 1 addition & 0 deletions packaging/openmix.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
IDI_ICON1 ICON "openmix.ico"
Binary file added resources/icons/openmix.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions resources/resources.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<RCC version="1.0">
<qresource prefix="/">
<file>styles/main.qss</file>
<file>icons/openmix.png</file>
<file>icons/chevron-up.svg</file>
<file>icons/chevron-down.svg</file>
</qresource>
Expand Down
2 changes: 1 addition & 1 deletion src/app/AutoUpdater.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace {
// Per-OS appcast feeds published by CI to GitHub Pages. Sparkle/WinSparkle poll
// these for new versions + signatures.
constexpr const char* kWindowsAppcast =
"https://johnqherman.github.io/OpenMix/appcast-windows.xml";
"https://openmix.dev/appcast-windows.xml";
} // namespace

AutoUpdater::AutoUpdater(QObject* parent) : QObject(parent) {}
Expand Down
6 changes: 3 additions & 3 deletions src/app/UpdateChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
namespace OpenMix {

namespace {
constexpr const char* kLatestReleaseApi =
"https://api.github.com/repos/johnqherman/OpenMix/releases/latest";
// small JSON published by CI: {"tag_name":"vX.Y.Z","html_url":"..."}
constexpr const char* kLatestReleaseApi = "https://openmix.dev/latest.json";
}

UpdateChecker::UpdateChecker(QObject* parent)
Expand Down Expand Up @@ -58,7 +58,7 @@ void UpdateChecker::checkForUpdates() {

QString url = obj.value("html_url").toString();
if (url.isEmpty())
url = "https://github.com/johnqherman/OpenMix/releases/latest";
url = "https://openmix.dev/download";

if (isNewer(tag, QCoreApplication::applicationVersion()))
emit updateAvailable(tag, url);
Expand Down
2 changes: 2 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "ui/WelcomeDialog.h"
#include "ui/theme/Theme.h"
#include <QApplication>
#include <QIcon>
#include <QSettings>
#include <QFile>
#include <QPalette>
Expand Down Expand Up @@ -37,6 +38,7 @@ int main(int argc, char* argv[]) {
QApplication::setApplicationName("OpenMix");
QApplication::setApplicationVersion("0.1.0");
QApplication::setOrganizationName("OpenMix");
QApplication::setWindowIcon(QIcon(":/icons/openmix.png"));

oclero::qlementine::icons::initializeIconTheme();

Expand Down
Loading
Loading