Skip to content

feat: ship portable ZIP alongside NSIS installer on Windows #53

feat: ship portable ZIP alongside NSIS installer on Windows

feat: ship portable ZIP alongside NSIS installer on Windows #53

Workflow file for this run

name: Build
on:
push:
branches: [master]
tags: ["v*"]
pull_request:
branches: [master]
jobs:
build-linux:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install non-Qt dependencies
run: |
sudo apt-get update
sudo apt-get install -y libxkbcommon-dev liblo-dev libasound2-dev build-essential cmake rpm file
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: "6.11.0"
host: "linux"
target: "desktop"
arch: "linux_gcc_64"
- name: ccache
uses: hendrikmuhs/ccache-action@v1
with:
key: ${{ runner.os }}-${{ hashFiles('**/CMakeLists.txt') }}
- name: Configure
run: cmake -B build -DCMAKE_BUILD_TYPE=Release
- name: Build
# 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
env:
QT_QPA_PLATFORM: offscreen
- name: Package (deb/rpm/tarball)
run: cd build && cpack
- name: Upload installers
uses: actions/upload-artifact@v4
with:
name: OpenMix-linux
path: |
build/*.deb
build/*.rpm
build/*.tar.gz
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Cache Qt
id: cache-qt
uses: actions/cache@v4
with:
path: C:\Qt\6.11.0\msvc2022_64
key: qt-6.11.0-msvc2022_64
- name: Download Qt
if: steps.cache-qt.outputs.cache-hit != 'true'
run: |
gh release download qt-deps --pattern "msvc2022_64.zip" --dir $env:TEMP
Expand-Archive -Path "$env:TEMP\msvc2022_64.zip" -DestinationPath C:\Qt\6.11.0
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install liblo via vcpkg
run: vcpkg install liblo:x64-windows
- name: Install NSIS
run: choco install nsis -y
- name: Set up MSVC
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: ccache
uses: hendrikmuhs/ccache-action@v1
with:
key: ${{ runner.os }}-${{ hashFiles('**/CMakeLists.txt') }}
- name: Configure
run: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake" -DCMAKE_PREFIX_PATH="C:\Qt\6.11.0\msvc2022_64"
- name: Build
run: cmake --build build --parallel
- name: Test
run: |
$env:PATH = "C:\Qt\6.11.0\msvc2022_64\bin;$env:VCPKG_INSTALLATION_ROOT\installed\x64-windows\bin;$env:PATH"
ctest --test-dir build --output-on-failure
env:
QT_QPA_PLATFORM: offscreen
- name: Package (NSIS installer)
run: |
$env:PATH = "C:\Qt\6.11.0\msvc2022_64\bin;C:\Program Files (x86)\NSIS;$env:PATH"
cd build
cpack -G "NSIS;ZIP"
- name: Upload installer
uses: actions/upload-artifact@v4
with:
name: OpenMix-windows
# explicit patterns so the raw build/OpenMix.exe binary isn't shipped
path: |
build/OpenMix-*-win64.exe
build/OpenMix-*-win64.zip
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: brew install qt@6 liblo
- name: ccache
uses: hendrikmuhs/ccache-action@v1
with:
key: ${{ runner.os }}-${{ hashFiles('**/CMakeLists.txt') }}
- name: Configure
run: |
export PATH="/opt/homebrew/opt/qt@6/bin:$PATH"
cmake -B build \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_PREFIX_PATH=/opt/homebrew/opt/qt@6
- name: Build
# 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
env:
QT_QPA_PLATFORM: offscreen
- name: Package (DMG)
run: |
export PATH="/opt/homebrew/opt/qt@6/bin:$PATH"
cd build && cpack -G DragNDrop
- name: Upload installer
uses: actions/upload-artifact@v4
with:
name: OpenMix-macos
path: build/*.dmg
release:
if: startsWith(github.ref, 'refs/tags/v')
needs: [build-linux, build-windows, build-macos]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Download artifacts
uses: actions/download-artifact@v4
- name: Create Release
uses: softprops/action-gh-release@v1
with:
files: |
OpenMix-linux/*
OpenMix-windows/*
OpenMix-macos/*
# 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]
runs-on: macos-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Install Sparkle tools
run: brew install --cask sparkle || brew install sparkle
- name: Download artifacts
uses: actions/download-artifact@v4
- name: Build appcasts + installer tree
env:
SPARKLE_KEY: ${{ secrets.OPENMIX_SPARKLE_ED_PRIVATE_KEY }}
TAG: ${{ github.ref_name }}
run: |
VERSION="${TAG#v}"
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=...
cat > site/appcast-macos.xml <<EOF
<?xml version="1.0"?>
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
<channel>
<item>
<title>OpenMix ${VERSION}</title>
<sparkle:version>${VERSION}</sparkle:version>
<sparkle:shortVersionString>${VERSION}</sparkle:shortVersionString>
<enclosure url="${BASE}/$(basename "$DMG")" ${SIG} sparkle:version="${VERSION}" type="application/octet-stream"/>
</item>
</channel>
</rss>
EOF
else
echo "::warning::No Sparkle key/tool/DMG; skipping macOS appcast (updates will not verify)"
fi
# Windows: WinSparkle RSS appcast
EXE=$(ls OpenMix-windows/*.exe | head -1)
if [ -n "$EXE" ]; then
cat > site/appcast-windows.xml <<EOF
<?xml version="1.0"?>
<rss xmlns:sparkle="http://www.andymatuschak.org/xml-namespaces/sparkle" version="2.0">
<channel>
<item>
<title>OpenMix ${VERSION}</title>
<sparkle:version>${VERSION}</sparkle:version>
<enclosure url="${BASE}/$(basename "$EXE")" sparkle:version="${VERSION}" type="application/octet-stream"/>
</item>
</channel>
</rss>
EOF
fi
# 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 --"