chore: bump version to 1.0.0, derive app version from CMake #66
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build | |
| on: | |
| push: | |
| branches: [master] | |
| tags: ["v*"] | |
| pull_request: | |
| branches: [master] | |
| jobs: | |
| build-linux: | |
| runs-on: ubuntu-24.04 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| # release tags must match project(VERSION) or artifacts ship with the | |
| # wrong version baked in; failing here blocks the release+appcast jobs | |
| - name: Check tag matches project version | |
| if: startsWith(github.ref, 'refs/tags/v') | |
| run: | | |
| TAG="${GITHUB_REF_NAME#v}" | |
| VER=$(sed -n 's/^ *VERSION \([0-9.]*\)$/\1/p' CMakeLists.txt | head -1) | |
| if [ "$TAG" != "$VER" ]; then | |
| echo "::error::Tag v${TAG} != project VERSION ${VER} in CMakeLists.txt" | |
| exit 1 | |
| fi | |
| - 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.2.23 | |
| 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@v7 | |
| with: | |
| name: OpenMix-linux | |
| path: | | |
| build/*.deb | |
| build/*.rpm | |
| build/*.tar.gz | |
| build-windows: | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Cache Qt | |
| id: cache-qt | |
| uses: actions/cache@v6 | |
| 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 | |
| # still node20 upstream (no node24 release as of v1.13.0); the runner | |
| # force-runs it on node24, which works but logs a deprecation warning | |
| uses: ilammy/msvc-dev-cmd@v1 | |
| with: | |
| arch: x64 | |
| - name: ccache | |
| uses: hendrikmuhs/ccache-action@v1.2.23 | |
| 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@v7 | |
| 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: | |
| # pinned: macos-latest is migrating to macOS 26 (June 2026); keep the | |
| # known-good image for the universal Qt build until 26 is validated | |
| runs-on: macos-15 | |
| steps: | |
| - uses: actions/checkout@v7 | |
| # runner image ships untrusted third-party taps (aws/tap) that make | |
| # every brew call (incl. ccache-action's) log a tap-trust warning | |
| - name: Remove untrusted brew taps | |
| run: brew untap aws/tap 2>/dev/null || true | |
| # official Qt macOS binaries are universal (arm64 + x86_64); brew's are | |
| # arm64-only, which would make the DMG Apple-silicon-only | |
| - name: Install Qt (universal) | |
| uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: "6.11.0" | |
| host: "mac" | |
| target: "desktop" | |
| arch: "clang_64" | |
| # brew liblo is arm64-only too; build it universal from source | |
| - name: Build liblo (universal) | |
| run: | | |
| curl -fsSL -o liblo.tar.gz https://downloads.sourceforge.net/liblo/liblo-0.31.tar.gz | |
| tar xf liblo.tar.gz | |
| cd liblo-0.31 | |
| ./configure --prefix="$HOME/liblo-universal" \ | |
| --disable-tests --disable-examples --disable-tools \ | |
| CFLAGS="-arch arm64 -arch x86_64 -mmacosx-version-min=12.0" | |
| make -j3 && make install | |
| - name: ccache | |
| uses: hendrikmuhs/ccache-action@v1.2.23 | |
| with: | |
| key: ${{ runner.os }}-universal-${{ hashFiles('**/CMakeLists.txt') }} | |
| - name: Configure | |
| run: | | |
| export PKG_CONFIG_PATH="$HOME/liblo-universal/lib/pkgconfig:$PKG_CONFIG_PATH" | |
| cmake -B build \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" \ | |
| -DCMAKE_OSX_DEPLOYMENT_TARGET=12.0 | |
| - 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: Verify universal binary | |
| run: | | |
| lipo -info build/OpenMix.app/Contents/MacOS/OpenMix | |
| lipo -info build/OpenMix.app/Contents/MacOS/OpenMix | grep -q x86_64 | |
| lipo -info build/OpenMix.app/Contents/MacOS/OpenMix | grep -q arm64 | |
| - name: Package (DMG) | |
| run: cd build && cpack -G DragNDrop | |
| - name: Upload installer | |
| uses: actions/upload-artifact@v7 | |
| 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@v8 | |
| - name: Create Release | |
| uses: softprops/action-gh-release@v3 | |
| 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-15 | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v7 | |
| - name: Remove untrusted brew taps | |
| run: brew untap aws/tap 2>/dev/null || true | |
| - name: Install Sparkle tools | |
| run: brew install --cask sparkle || brew install sparkle | |
| - name: Download artifacts | |
| uses: actions/download-artifact@v8 | |
| - 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 --" |