Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/release-linux.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
# while POCO will be linked statically.
name: Build Release Package for Linux

on: workflow_dispatch
on:
workflow_dispatch:
workflow_call:

jobs:
build-deb:
Expand Down
201 changes: 190 additions & 11 deletions .github/workflows/release-macos.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,25 @@
# Builds a universal binary on macOS.
name: Build Release Package for macOS

on: workflow_dispatch
on:
workflow_dispatch:
workflow_call:
secrets:
MACOS_CERTIFICATE_APPLICATION:
required: true
MACOS_CERTIFICATE_INSTALLER:
required: true
MACOS_CERTIFICATE_PASSWORD:
required: true
MACOS_NOTARY_API_KEY:
required: true
MACOS_NOTARY_KEY_ID:
required: true
MACOS_NOTARY_ISSUER_ID:
required: true

jobs:
build-deb:
build-pkg:
name: ProductBuild Installer, macOS x86_64+arm64
runs-on: macos-latest

Expand Down Expand Up @@ -102,6 +117,27 @@ jobs:
repository: projectM-visualizer/presets-milkdrop-texture-pack
path: presets-milkdrop-texture-pack

- name: Import Code Signing Certificates
env:
MACOS_CERTIFICATE_APPLICATION: ${{ secrets.MACOS_CERTIFICATE_APPLICATION }}
MACOS_CERTIFICATE_INSTALLER: ${{ secrets.MACOS_CERTIFICATE_INSTALLER }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
run: |
echo "$MACOS_CERTIFICATE_APPLICATION" | base64 --decode > app_cert.p12 && chmod 600 app_cert.p12
echo "$MACOS_CERTIFICATE_INSTALLER" | base64 --decode > installer_cert.p12 && chmod 600 installer_cert.p12

KEYCHAIN_PASSWORD=$(openssl rand -base64 32)
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain

security import app_cert.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security import installer_cert.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PASSWORD" -T /usr/bin/productsign

security set-key-partition-list -S apple-tool:,apple:,codesign:,productbuild: -s -k "$KEYCHAIN_PASSWORD" build.keychain

rm app_cert.p12 installer_cert.p12

- name: Build projectMSDL
run: |
mkdir cmake-build-frontend-sdl2
Expand All @@ -112,19 +148,162 @@ jobs:
"-DCMAKE_PREFIX_PATH=${GITHUB_WORKSPACE}/install-libprojectm;${GITHUB_WORKSPACE}/install-poco;${GITHUB_WORKSPACE}/install-libsdl2" \
"-DPRESET_DIRS=${{ github.workspace }}/presets-cream-of-the-crop" \
"-DTEXTURE_DIRS=${{ github.workspace }}/presets-milkdrop-texture-pack/textures" \
'-DDEFAULT_CONFIG_PATH=${application.dir}/../share/projectMSDL/' \
'-DDEFAULT_PRESETS_PATH=${application.dir}/../share/projectMSDL/presets/' \
'-DDEFAULT_TEXTURES_PATH=${application.dir}/../share/projectMSDL/textures/' \
-DENABLE_INSTALL_BDEPS=ON
cmake --build cmake-build-frontend-sdl2 --parallel
cmake --install cmake-build-frontend-sdl2 --prefix "${{ github.workspace }}/install"

- name: Sign Application Bundle
run: |
APP_PATH="${{ github.workspace }}/install/projectM.app"
IDENTITY="Developer ID Application: Mischa Spiegelmock (5926VBQM6Y)"
ENTITLEMENTS="${{ github.workspace }}/frontend-sdl2/src/resources/projectMSDL.entitlements"

# Sign frameworks first (SDL2, Poco, etc.)
if [ -d "$APP_PATH/Contents/Frameworks" ]; then
find "$APP_PATH/Contents/Frameworks" \( -name "*.dylib" -o -name "*.framework" \) -exec \
codesign --force --options runtime --sign "$IDENTITY" {} \;
fi

# Sign plugins if present
if [ -d "$APP_PATH/Contents/PlugIns" ]; then
find "$APP_PATH/Contents/PlugIns" -name "*.dylib" -exec \
codesign --force --options runtime --sign "$IDENTITY" {} \;
fi

- name: Package projectMSDL
# Sign the main executable with entitlements
codesign --force --options runtime --entitlements "$ENTITLEMENTS" --sign "$IDENTITY" \
"$APP_PATH/Contents/MacOS/projectM"

# Sign the entire bundle with entitlements
codesign --force --options runtime --entitlements "$ENTITLEMENTS" --sign "$IDENTITY" "$APP_PATH"

# Verify
codesign --verify --deep --strict "$APP_PATH"

- name: Notarize Application Bundle
env:
API_KEY_BASE64: ${{ secrets.MACOS_NOTARY_API_KEY }}
API_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }}
API_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }}
run: |
cd cmake-build-frontend-sdl2
cpack -G productbuild
mkdir -p ~/.private_keys
echo "$API_KEY_BASE64" | base64 --decode > ~/.private_keys/AuthKey_${API_KEY_ID}.p8
chmod 600 ~/.private_keys/AuthKey_${API_KEY_ID}.p8

ditto -c -k --keepParent \
"${{ github.workspace }}/install/projectM.app" \
"projectM-notarize.zip"

xcrun notarytool submit "projectM-notarize.zip" \
--key ~/.private_keys/AuthKey_${API_KEY_ID}.p8 \
--key-id "$API_KEY_ID" \
--issuer "$API_ISSUER_ID" \
--wait

- name: Upload Artifact
xcrun stapler staple "${{ github.workspace }}/install/projectM.app"

- name: Upload .app Bundle Artifact
uses: actions/upload-artifact@v4
with:
name: projectMSDL-macOS-Universal
path: cmake-build-frontend-sdl2/*.pkg
name: projectMSDL-macOS-Universal-APP
path: install/projectM.app/

- name: Create projectMSDL PKG Installer
run: |
# Get version from CMake cache
VERSION=$(grep 'CMAKE_PROJECT_VERSION:STATIC=' cmake-build-frontend-sdl2/CMakeCache.txt | grep -Eo '([0-9.]+)')

# Build component package from signed app
pkgbuild \
--root "${{ github.workspace }}/install" \
--identifier "org.projectm-visualizer.projectmsdl" \
--version "$VERSION" \
--install-location "/Applications" \
--component-plist "frontend-sdl2/src/resources/projectMSDL-component.plist" \
"projectMSDL-component.pkg"

# Build unsigned product archive
productbuild \
--distribution "frontend-sdl2/src/resources/distribution.xml" \
--package-path "." \
--resources "frontend-sdl2/src/resources" \
"projectM-${VERSION}-macOS-universal-unsigned.pkg"

# Sign the package with productsign
productsign \
--sign "Developer ID Installer: Mischa Spiegelmock (5926VBQM6Y)" \
"projectM-${VERSION}-macOS-universal-unsigned.pkg" \
"projectM-${VERSION}-macOS-universal.pkg"

rm "projectM-${VERSION}-macOS-universal-unsigned.pkg"

- name: Notarize PKG Installer
env:
API_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }}
API_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }}
run: |
PKG_FILE=$(ls projectM-*.pkg | head -1)

xcrun notarytool submit "$PKG_FILE" \
--key ~/.private_keys/AuthKey_${API_KEY_ID}.p8 \
--key-id "$API_KEY_ID" \
--issuer "$API_ISSUER_ID" \
--wait

xcrun stapler staple "$PKG_FILE"

- name: Upload PKG Installer Artifact
uses: actions/upload-artifact@v4
with:
name: projectMSDL-macOS-Universal-PKG
path: projectM-*.pkg

- name: Create projectMSDL DMG Image
run: |
# Get version from CMake cache
VERSION=$(grep 'CMAKE_PROJECT_VERSION:STATIC=' cmake-build-frontend-sdl2/CMakeCache.txt | grep -Eo '([0-9.]+)')

# Install create-dmg
brew install create-dmg

# Build and sign DMG image
create-dmg \
--volname "projectM Installer" \
--volicon "frontend-sdl2/src/resources/icons/icon.icns" \
--background "frontend-sdl2/src/resources/dmg_background.png" \
--window-pos 200 120 \
--window-size 800 400 \
--icon-size 100 \
--icon "projectM.app" 200 190 \
--hide-extension "projectM.app" \
--app-drop-link 600 185 \
--codesign "Developer ID Application: Mischa Spiegelmock (5926VBQM6Y)" \
"projectM-${VERSION}-macOS-universal.dmg" \
"install/"

- name: Notarize DMG Installer
env:
API_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }}
API_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }}
run: |
DMG_FILE=$(ls projectM-*.dmg | head -1)

xcrun notarytool submit "$DMG_FILE" \
--key ~/.private_keys/AuthKey_${API_KEY_ID}.p8 \
--key-id "$API_KEY_ID" \
--issuer "$API_ISSUER_ID" \
--wait

xcrun stapler staple "$DMG_FILE"

- name: Upload DMG Installer Artifact
uses: actions/upload-artifact@v4
with:
name: projectMSDL-macOS-Universal-DMG
path: projectM-*.dmg

- name: Cleanup Notarization API Key
if: ${{ always() }}
env:
API_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }}
run: rm -f ~/.private_keys/AuthKey_${API_KEY_ID}.p8
4 changes: 3 additions & 1 deletion .github/workflows/release-windows.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
# including projectM.
name: Build Release Package for Windows

on: workflow_dispatch
on:
workflow_dispatch:
workflow_call:

jobs:
build:
Expand Down
55 changes: 55 additions & 0 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Unified release workflow
# Triggers on version tags, builds all platforms, and creates a GitHub release
name: Release

on:
push:
tags:
- 'v*'

jobs:
build-linux:
name: Build Linux
uses: ./.github/workflows/release-linux.yaml

build-windows:
name: Build Windows
uses: ./.github/workflows/release-windows.yaml

build-macos:
name: Build macOS
uses: ./.github/workflows/release-macos.yaml
secrets:
MACOS_CERTIFICATE_APPLICATION: ${{ secrets.MACOS_CERTIFICATE_APPLICATION }}
MACOS_CERTIFICATE_INSTALLER: ${{ secrets.MACOS_CERTIFICATE_INSTALLER }}
MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
MACOS_NOTARY_API_KEY: ${{ secrets.MACOS_NOTARY_API_KEY }}
MACOS_NOTARY_KEY_ID: ${{ secrets.MACOS_NOTARY_KEY_ID }}
MACOS_NOTARY_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }}

create-release:
name: Create Release
needs: [build-linux, build-windows, build-macos]
runs-on: ubuntu-latest
permissions:
contents: write

steps:
- name: Download all artifacts
uses: actions/download-artifact@v4
with:
path: artifacts

- name: Display artifacts
run: ls -R artifacts

- name: Create Release
uses: softprops/action-gh-release@v2
with:
generate_release_notes: true
files: |
artifacts/**/*.pkg
artifacts/**/*.deb
artifacts/**/*.tar.gz
artifacts/**/*.zip
artifacts/**/*.msi
4 changes: 2 additions & 2 deletions packaging-macos.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/src/resources/gpl-3
set(CPACK_STRIP_FILES TRUE)

### Productbuild configuration
set(CPACK_PKGBUILD_IDENTITY_NAME "${CODESIGN_IDENTITY_INSTALLER}")
set(CPACK_PRODUCTBUILD_IDENTITY_NAME "${CODESIGN_IDENTITY_INSTALLER}")
set(CPACK_PKGBUILD_IDENTITY_NAME "$ENV{CODESIGN_IDENTITY_INSTALLER}")
set(CPACK_PRODUCTBUILD_IDENTITY_NAME "$ENV{CODESIGN_IDENTITY_INSTALLER}")
set(CPACK_PRODUCTBUILD_IDENTIFIER "org.projectm-visualizer.projectmsdl")

string(REPLACE ";" "," INSTALL_ARCHITECTURES "${CMAKE_OSX_ARCHITECTURES}")
Expand Down
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ set_target_properties(projectMSDL PROPERTIES
MACOSX_BUNDLE_GUI_IDENTIFIER "org.projectm.frontend.sdl2"
MACOSX_BUNDLE_ICON_FILE "projectMSDL.icns"
MACOSX_BUNDLE_SHORT_VERSION_STRING "${projectMSDL_VERSION}"
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/resources/Info.plist.in"
)

if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
Expand Down
30 changes: 30 additions & 0 deletions src/resources/Info.plist.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>@MACOSX_BUNDLE_EXECUTABLE_NAME@</string>
<key>CFBundleIconFile</key>
<string>@MACOSX_BUNDLE_ICON_FILE@</string>
<key>CFBundleIdentifier</key>
<string>@MACOSX_BUNDLE_GUI_IDENTIFIER@</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>@MACOSX_BUNDLE_BUNDLE_NAME@</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>@MACOSX_BUNDLE_SHORT_VERSION_STRING@</string>
<key>CFBundleVersion</key>
<string>@MACOSX_BUNDLE_BUNDLE_VERSION@</string>
<key>NSHumanReadableCopyright</key>
<string>@MACOSX_BUNDLE_COPYRIGHT@</string>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSMicrophoneUsageDescription</key>
<string>projectM requires microphone access to visualize audio input.</string>
</dict>
</plist>
17 changes: 17 additions & 0 deletions src/resources/distribution.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<installer-gui-script minSpecVersion="2">
<title>projectM</title>
<organization>org.projectm-visualizer</organization>
<welcome file="macos-welcome.txt"/>
<readme file="macos-readme.txt"/>
<license file="gpl-3.0.rtf"/>
<options customize="never" require-scripts="false" hostArchitectures="x86_64,arm64"/>
<domains enable_anywhere="false" enable_currentUserHome="false" enable_localSystem="true"/>
<choices-outline>
<line choice="default"/>
</choices-outline>
<choice id="default" title="projectM">
<pkg-ref id="org.projectm-visualizer.projectmsdl"/>
</choice>
<pkg-ref id="org.projectm-visualizer.projectmsdl" version="0" onConclusion="none">projectMSDL-component.pkg</pkg-ref>
</installer-gui-script>
Binary file added src/resources/dmg_background.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/resources/projectMSDL-component.plist
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<key>BundleOverwriteAction</key>
<string>upgrade</string>
<key>RootRelativeBundlePath</key>
<string>Applications/projectM.app</string>
<string>projectM.app</string>
</dict>
</array>
</plist>
</plist>
8 changes: 8 additions & 0 deletions src/resources/projectMSDL.entitlements
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.device.audio-input</key>
<true/>
</dict>
</plist>
Loading