diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 87e6cb5..1d2a36b 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -3,7 +3,7 @@ on: push: branches: - '**' - + concurrency: group: build-${{ github.ref || github.run_id }} @@ -11,33 +11,58 @@ jobs: build: name: Build runs-on: ${{ matrix.os }} - timeout-minutes: 30 + timeout-minutes: 60 strategy: + fail-fast: false matrix: include: - name: Windows - os: [windows-latest] + os: windows-latest - name: Linux - os: [ubuntu-latest] + os: ubuntu-latest - name: macOS - os: [macos-latest] + os: macos-latest steps: - uses: actions/checkout@v4 with: submodules: recursive - - name: "Run script" + + - name: Install Linux dependencies + if: matrix.name == 'Linux' run: | - ./ci/build.sh + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + ninja-build clang g++ \ + libasound2-dev libjack-jackd2-dev ladspa-sdk libcurl4-openssl-dev \ + libfreetype-dev libfontconfig1-dev \ + libx11-dev libxcomposite-dev libxcursor-dev libxext-dev \ + libxinerama-dev libxrandr-dev libxrender-dev \ + libwebkit2gtk-4.1-dev libglu1-mesa-dev mesa-common-dev + shell: bash + + - name: Install Inno Setup (Windows) + if: matrix.name == 'Windows' + run: choco install innosetup --no-progress -y + shell: bash + + - name: Build installer + run: ./Installer/build.sh shell: bash env: + # macOS signing / notarization APPLICATION: ${{ secrets.APPLICATION }} - INSTALLER: ${{ secrets.INSTALLER }} - APPLE_PASS: ${{ secrets.APPLE_PASS }} - APPLE_USER: ${{ secrets.APPLE_USER }} - APIKEY: ${{ secrets.APIKEY }} + INSTALLER: ${{ secrets.INSTALLER }} + APPLE_USER: ${{ secrets.APPLE_USER }} + APPLE_PASS: ${{ secrets.APPLE_PASS }} + # Windows Trusted Signing (Azure) + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + - name: Upload Artifact uses: actions/upload-artifact@v4 + if: always() with: name: Binaries ${{ matrix.name }} - path: ci/bin/*.zip - retention-days: 30 \ No newline at end of file + path: bin/* + retention-days: 30 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 3862267..491a0ef 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -8,45 +8,58 @@ jobs: build: name: Build runs-on: ${{ matrix.os }} - timeout-minutes: 30 + timeout-minutes: 60 strategy: + fail-fast: false matrix: include: - name: Windows - os: [windows-latest] + os: windows-latest - name: Linux - os: [ubuntu-latest] + os: ubuntu-latest - name: macOS - os: [macos-latest] + os: macos-latest steps: - uses: actions/checkout@v4 with: submodules: recursive - - name: "Build" + + - name: Install Linux dependencies + if: matrix.name == 'Linux' run: | - ./ci/build.sh + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + ninja-build clang g++ \ + libasound2-dev libjack-jackd2-dev ladspa-sdk libcurl4-openssl-dev \ + libfreetype-dev libfontconfig1-dev \ + libx11-dev libxcomposite-dev libxcursor-dev libxext-dev \ + libxinerama-dev libxrandr-dev libxrender-dev \ + libwebkit2gtk-4.1-dev libglu1-mesa-dev mesa-common-dev + shell: bash + + - name: Install Inno Setup (Windows) + if: matrix.name == 'Windows' + run: choco install innosetup --no-progress -y + shell: bash + + - name: Build installer + run: ./Installer/build.sh shell: bash env: APPLICATION: ${{ secrets.APPLICATION }} - INSTALLER: ${{ secrets.INSTALLER }} - APPLE_PASS: ${{ secrets.APPLE_PASS }} - APPLE_USER: ${{ secrets.APPLE_USER }} - APIKEY: ${{ secrets.APIKEY }} - - name: "Upload" - run: | - ./ci/upload.sh - shell: bash - env: - APPLICATION: ${{ secrets.APPLICATION }} - INSTALLER: ${{ secrets.INSTALLER }} - APPLE_PASS: ${{ secrets.APPLE_PASS }} - APPLE_USER: ${{ secrets.APPLE_USER }} - APIKEY: ${{ secrets.APIKEY }} + INSTALLER: ${{ secrets.INSTALLER }} + APPLE_USER: ${{ secrets.APPLE_USER }} + APPLE_PASS: ${{ secrets.APPLE_PASS }} + AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }} + AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }} + AZURE_CLIENT_SECRET: ${{ secrets.AZURE_CLIENT_SECRET }} + - name: Upload Artifact uses: actions/upload-artifact@v4 + if: always() with: name: Binaries ${{ matrix.name }} - path: ci/bin/*.zip + path: bin/* retention-days: 30 release: @@ -54,7 +67,6 @@ jobs: needs: build runs-on: ubuntu-latest timeout-minutes: 30 - steps: - uses: actions/checkout@v4 - name: Download Artifacts @@ -64,3 +76,4 @@ jobs: shell: bash env: GH_TOKEN: ${{ secrets.ACCESS_TOKEN }} + APIKEY: ${{ secrets.APIKEY }} diff --git a/.gitignore b/.gitignore index 3cdcb84..addd9fd 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,8 @@ xcuserdata Builds JuceLibraryCode -ci/bin +bin +Installer/macOS/bin +Installer/win/bin .DS_Store .claude \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 52f0a96..0262d5c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,10 @@ set (PLUGIN_CODE Wave) set (CMAKE_POLICY_DEFAULT_CMP0077 NEW) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") set (CMAKE_SUPPRESS_REGENERATION true) -set (CMAKE_SKIP_INSTALL_RULES YES) +# Skip install rules everywhere except Linux, where CPack/DEB needs them. +if (NOT (UNIX AND NOT APPLE)) + set (CMAKE_SKIP_INSTALL_RULES YES) +endif() set (CMAKE_POSITION_INDEPENDENT_CODE ON) set_property (GLOBAL PROPERTY DEBUG_CONFIGURATIONS "Debug") @@ -96,9 +99,7 @@ set_property (TARGET melatonin_inspector APPEND PROPERTY LABELS melatonin) set_property (DIRECTORY APPEND PROPERTY LABELS Assets) file (GLOB_RECURSE binary_files CONFIGURE_DEPENDS - "./plugin/Resources/layout.json" - "./plugin/Resources/WavetablesFLAC/*.wt2048" - "./plugin/Resources/Presets/*.xml") + "./plugin/Resources/layout.json") juce_add_binary_data (${PLUGIN_NAME}_Assets SOURCES ${binary_files}) @@ -242,10 +243,57 @@ if(UNIX AND NOT APPLE) endif() -if(WIN32) - set (dest "Program Files") -else() - set (dest "Applications") -endif() +# +# Install + CPack (Linux only — macOS uses pkgbuild/productbuild, Windows uses Inno Setup) +# +if (UNIX AND NOT APPLE) + set (_artefact_dir "${CMAKE_BINARY_DIR}/${PLUGIN_NAME}_artefacts/$") -install (TARGETS ${PLUGIN_NAME} DESTINATION "${dest}") + install (FILES "${_artefact_dir}/VST/lib${PLUGIN_NAME}.so" + DESTINATION lib/vst + RENAME "${PLUGIN_NAME}.so" + COMPONENT VST) + + install (DIRECTORY "${_artefact_dir}/VST3/${PLUGIN_NAME}.vst3" + DESTINATION lib/vst3 + COMPONENT VST3 + USE_SOURCE_PERMISSIONS) + + install (DIRECTORY "${_artefact_dir}/LV2/${PLUGIN_NAME}.lv2" + DESTINATION lib/lv2 + COMPONENT LV2 + USE_SOURCE_PERMISSIONS) + + install (FILES "${_artefact_dir}/CLAP/${PLUGIN_NAME}.clap" + DESTINATION lib/clap + COMPONENT CLAP) + + install (DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/plugin/Resources/Presets/" + DESTINATION share/SocaLabs/${PLUGIN_NAME}/Presets + COMPONENT Resources + FILES_MATCHING PATTERN "*.xml") + + install (DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/plugin/Resources/WavetablesFLAC/" + DESTINATION share/SocaLabs/${PLUGIN_NAME}/Wavetables + COMPONENT Resources + FILES_MATCHING PATTERN "*.wt2048") + + set (CPACK_PACKAGE_NAME "wavetable") + set (CPACK_PACKAGE_VERSION "${PLUGIN_VERSION}") + set (CPACK_PACKAGE_VENDOR "SocaLabs") + set (CPACK_PACKAGE_HOMEPAGE_URL "https://socalabs.com/") + set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "Wavetable synthesizer plugin") + set (CPACK_PACKAGE_CONTACT "support@socalabs.com") + set (CPACK_PACKAGING_INSTALL_PREFIX "/usr") + set (CPACK_GENERATOR "DEB") + set (CPACK_DEB_COMPONENT_INSTALL OFF) + set (CPACK_COMPONENTS_ALL VST VST3 LV2 CLAP Resources) + set (CPACK_COMPONENTS_GROUPING ALL_COMPONENTS_IN_ONE) + set (CPACK_DEBIAN_PACKAGE_MAINTAINER "Roland Rabien ") + set (CPACK_DEBIAN_PACKAGE_SECTION "sound") + set (CPACK_DEBIAN_PACKAGE_PRIORITY "optional") + set (CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) + set (CPACK_DEBIAN_FILE_NAME "Wavetable.deb") + + include (CPack) +endif() diff --git a/Installer/EULA.rtf b/Installer/EULA.rtf new file mode 100644 index 0000000..7eb667d --- /dev/null +++ b/Installer/EULA.rtf @@ -0,0 +1,127 @@ +{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang4105\deflangfe4105{\fonttbl{\f0\fswiss\fprq2\fcharset0 Calibri Light;}} +{\colortbl ;\red82\green82\blue82;\red0\green0\blue0;} +{\*\listtable +{\list\listhybrid +{\listlevel\levelnfc0\leveljc0\levelstartat1{\leveltext\'02\'00.;}{\levelnumbers\'01;}\jclisttab\tx720} +{\listlevel\levelnfc4\leveljc0\levelstartat1{\leveltext\'02\'01.;}{\levelnumbers\'01;}\jclisttab\tx720}\listid1 }} +{\*\listoverridetable{\listoverride\listid1\listoverridecount0\ls1}} +{\*\generator Riched20 10.0.18362}{\*\mmathPr\mnaryLim0\mdispDef1\mwrapIndent1440 }\viewkind4\uc1 +\pard\widctlpar\expndtw-10\kerning28\f0\fs56 End-User Licence Agreement\par + +\pard\widctlpar\sa720\qj\cf1\expndtw0\kerning0\fs21\line READ THE FOLLOWING LICENCE AGREEMENT CAREFULLY! IT CONTAINS VERY IMPORTANT INFORMATION ABOUT YOUR RIGHTS AND OBLIGATIONS, AS WELL AS LIMITATIONS AND EXCLUSIONS THAT MAY APPLY TO YOU. THIS DOCUMENT CONTAINS A DISPUTE RESOLUTION CLAUSE. BY CLICKING ON THE "ACCEPT" BUTTON, YOU ARE CONSENTING TO BE BOUND BY AND ARE BECOMING A PARTY TO THIS AGREEMENT. IF YOU DO NOT AGREE TO ALL OF THE TERMS OF THIS AGREEMENT, CLICK THE "DO NOT ACCEPT" BUTTON OR LEAVE THE WEBSITE.\par + +\pard\widctlpar\sa120\qj\cf2\b\fs33 1. Definitions\par + +\pard +{\pntext\f0 1.\tab}{\*\pn\pnlvlbody\pnf0\pnindent720\pnstart1\pndec{\pntxta.}} +\widctlpar\fi-360\li360\qj\cf1\b0\fs21 "We", "Our" and "Us" means SocaLabs.\par +{\pntext\f0 2.\tab}"You" and "Your" means the person or entity who is being licenced to use the Licenced Software.\par +{\pntext\f0 3.\tab}"Agreement" means this Software Licence Agreement between Us and You;\par +{\pntext\f0 4.\tab}"Licenced Materials" means the Licenced Software and the Licenced Library;\par +{\pntext\f0 5.\tab}"Licenced Software" means certain commercial software products being provided to You under this Agreement, including executable program modules thereof, as well as related documentation, and computer readable media;\par +{\pntext\f0 6.\tab}"Sub-Licenced Software" means certain third party owned software components being provided under this Agreement that are required to properly enable or operate the Licenced Software; and\par +{\pntext\f0 7.\tab}"Licenced Library" means any audio waveforms, such as melodies, music, instrument recordings, or other audible information, provided by Us for use with the Licenced Software.\par + +\pard\widctlpar\li360\qj\par +\par + +\pard\widctlpar\sa120\qj\cf2\b\fs33 2. Software Licence, Rights and Restrictions\par + +\pard +{\listtext\f0 1.\tab}\jclisttab\tx720\ls1\widctlpar\fi-360\li360\qj\cf1\b0\fs21 Subject to the provisions contained in this Agreement, We hereby grant to You a personal, revocable, non-exclusive licence (the "Licence") to access, read, use and download one copy of the Licenced Materials solely for the purposes provided under paragraph 3. This is not a transfer of title.\par +{\listtext\f0 2.\tab}Under the Licence you may not and shall not, without Our express written authorization:\par + +\pard +{\listtext\f0 a.\tab}\jclisttab\tx720\ls1\ilvl1\widctlpar\fi-360\li1080\qj own title, or transfer title to the Licenced Materials to another party;\par +{\listtext\f0 b.\tab}transfer the Licence;\par +{\listtext\f0 c.\tab}remove any copyright or other proprietary notations from the Licenced Materials;\par +{\listtext\f0 d.\tab}distribute, or sublicense or otherwise provide copies of or access to the Licenced Materials to any third party;\par +{\listtext\f0 e.\tab}pledge, hypothecate, alienate or otherwise encumber the Licenced Materials to any third party;\par +{\listtext\f0 f.\tab}create derivative works of, reverse engineer, decompile, disassemble, adapt, translate, transmit, arrange, modify, copy, bundle, sell, sublicense, export, merge, transfer, adapt, loan, rent, lease, assign, share, outsource, host, publish, make available to any person or otherwise use, either directly or indirectly, the Licenced Materials;\par +{\listtext\f0 g.\tab}permit, allow or do anything that would infringe or otherwise prejudice the proprietary rights of Us or Our licensors;\par +{\listtext\f0 h.\tab}circumvent, remove, alter or otherwise impair any technological protection measures, digital rights management information, or other copy-protection mechanisms used in or by the Licenced Materials; or\par +{\listtext\f0 i.\tab}include, use, re-sample, mix, process, isolate or embed any content from the Licenced Library in any virtual instrument, audio sampling system, audio playback system or library of any kind, other than those provided or authorized by Us.\par + +\pard +{\listtext\f0 3.\tab}\jclisttab\tx720\ls1\widctlpar\fi-360\li360\qj We are not obligated to provide any authorization referred to in paragraph 2(2), We reserve the right to charge a fee for the grant of such authorization, and We may cancel such authorization at Our sole and unfettered discretion by providing notice to You of such cancellation.\par +{\listtext\f0 4.\tab}The restrictions set out in this Agreement, including the restrictions listed at paragraph 2(2), shall not apply to the limited extent the restrictions are prohibited by applicable law.\par +{\listtext\f0 5.\tab}We will have the right to inspect and enforce the restrictions and covenants contained in this Agreement at Your sole expense, and You hereby agree to promptly notify Us of any known violations of such restrictions.\par +{\listtext\f0 6.\tab}You agree to protect the Licenced Materials from unauthorized use, reproduction, distribution or publication in electronic or physical form.\par +{\listtext\f0 7.\tab}Upon execution of this Agreement, We will:\par + +\pard +{\listtext\f0 a.\tab}\jclisttab\tx720\ls1\ilvl1\widctlpar\fi-360\li1080\qj permit You to download a copy of the Licenced Materials for Your use under this Agreement; and\par +{\listtext\f0 b.\tab}permit You to download updates to the Licenced Software as We consider needed. We are not obligated to provide updates for the Licenced Software.\par + +\pard\widctlpar\li1080\qj\par +\par + +\pard\widctlpar\sa120\qj\cf2\b\fs33 3. Usage Conditions\par + +\pard +{\pntext\f0 1.\tab}{\*\pn\pnlvlbody\pnf0\pnindent720\pnstart1\pndec{\pntxta.}} +\widctlpar\fi-360\li360\qj\cf1\b0\fs21 You are authorized to use the Licenced Materials only for personal use. Your personal use may be commercial.\par +{\pntext\f0 2.\tab}You may not rent out to others equipment on which the Licenced Software is installed or by which the Licenced Library is accessible without obtaining a written rental licence from Us that expressly authorizes such use. We reserve the right to charge a fee for a rental licence.\par +{\pntext\f0 3.\tab}You may install the Licenced Software on as many devices as required for Your personal use provided such devices are used only by You.\par + +\pard\widctlpar\qj\par + +\pard\widctlpar\li360\qj\par + +\pard\widctlpar\sa120\qj\cf2\b\fs33 4. Ownership and Intellectual Property Rights\par + +\pard +{\pntext\f0 1.\tab}{\*\pn\pnlvlbody\pnf0\pnindent720\pnstart1\pndec{\pntxta.}} +\widctlpar\fi-360\li360\qj\cf1\b0\fs21 All copyright in the Licenced Software, including any documentation, media, packaging and illustrations, but excluding the Sublicensed Software, is owned by Us. You do not acquire title to any copyright in the Licenced Materials or Sublicensed Software under this Agreement.\par +{\pntext\f0 2.\tab}Certain logos, product names and trade-marks owned by Us or by third parties may be contained within the Licenced Materials or in documentation, media, packaging and illustrations relating to the Licensed Software. You do not acquire title to any such logos, product names or trade-marks under this Agreement.\par + +\pard\widctlpar\li360\qj\par +\par + +\pard\widctlpar\sa120\qj\cf2\b\fs33 5. Termination\par + +\pard +{\pntext\f0 1.\tab}{\*\pn\pnlvlbody\pnf0\pnindent720\pnstart1\pndec{\pntxta.}} +\widctlpar\fi-360\li360\qj\cf1\b0\fs21 This Agreement is effective until terminated by Us, with or without cause, in Our sole and unfettered discretion. We may terminate this Agreement without notice to You if You fail to comply with any of its terms. Any such termination by Us shall be in addition to and without prejudice to such rights and remedies as may be available to Us, including injunction and other equitable remedies.\par +{\pntext\f0 2.\tab}The disclaimers, limitations on liability, ownership, termination, interpretation, Your warranty and the indemnity provisions of this Agreement shall survive the termination or expiry of this Agreement.\par +{\pntext\f0 3.\tab}This Agreement will automatically terminate if You violate or assist in the violation of any of the restrictions of paragraph 2(2).\par +{\pntext\f0 4.\tab}On the termination of this Agreement, you must destroy any copies of the Licenced Materials in your possession whether in electronic or printed format.\par + +\pard\widctlpar\li360\qj\par +\par + +\pard\widctlpar\sa120\qj\cf2\b\fs33 6. Indemnification\par + +\pard\widctlpar\sa720\qj\cf1\b0\fs21 You agree to indemnify, defend and hold Us and Our partners, lawyers, staff, affiliates, successors and assigns (collectively, \ldblquote Affiliated Parties\rdblquote ) harmless from any liability, loss, claim and expense, including reasonable legal fees, related to Your violation of this Agreement or use of the Licenced Materials.\par + +\pard\widctlpar\sa120\qj\cf2\b\fs33 7. Disclaimer and Limitation of Liability\par + +\pard +{\pntext\f0 1.\tab}{\*\pn\pnlvlbody\pnf0\pnindent720\pnstart1\pndec{\pntxta.}} +\widctlpar\fi-360\li360\qj\cf1\b0\fs21 THE LICENCED MATERIALS ARE PROVIDED "AS IS", "AS AVAILABLE" AND ALL WARRANTIES, EXPRESS OR IMPLIED, ARE DISCLAIMED (INCLUDING BUT NOT LIMITED TO THE DISCLAIMER OF ANY IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE). THE LICENCED MATERIALS MAY CONTAIN BUGS, ERRORS, PROBLEMS OR OTHER LIMITATIONS. WE AND OUR AFFILIATED PARTIES HAVE NO LIABILITY WHATSOEVER FOR YOUR USE OF ANY OF THE MATERIALS. IN PARTICULAR, BUT NOT AS A LIMITATION THEREOF, WE AND THE AFFILIATED PARTIES ARE NOT LIABLE FOR ANY INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING DAMAGES FOR LOSS OF BUSINESS, LOSS OF PROFITS, LITIGATION OR THE LIKE), WHETHER BASED ON BREACH OF CONTRACT, BREACH OF WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCT LIABILITY OR OTHERWISE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE NEGATION OF DAMAGES SET FORTH ABOVE ARE FUNDAMENTAL ELEMENTS OF THE BASIS OF THE BARGAIN BETWEEN YOU AND US. THE LICENCED MATERIALS WOULD NOT BE PROVIDED WITHOUT SUCH LIMITATIONS.\par +{\pntext\f0 2.\tab}All responsibility or liability for any damages caused by the Licenced Materials, including, without limitation, damages caused by computer viruses or other malicious code contained within the Licenced Software is disclaimed.\par + +\pard\widctlpar\li360\qj\par +\par + +\pard\widctlpar\sa120\qj\cf2\b\fs33 8. Use of Information\par + +\pard\widctlpar\sa720\qj\cf1\b0\fs21 We reserve the right, and You authorize Us, to the use and assignment of all information regarding Your use of the Licenced Materials and all information provided by You in any manner consistent with Our Privacy Policy.\par + +\pard\widctlpar\sa120\qj\cf2\b\fs33 9. Miscellaneous\par + +\pard +{\pntext\f0 1.\tab}{\*\pn\pnlvlbody\pnf0\pnindent720\pnstart1\pndec{\pntxta.}} +\widctlpar\fi-360\li360\qj\cf1\b0\fs21 This Agreement shall be treated as though it were executed and performed in the province of British Columbia, Canada, and shall be governed by and construed in accordance with the laws of the province of British Columbia (without regard to conflict of law principles).\par +{\pntext\f0 2.\tab}Any of Your causes of action with respect to the Licenced Materials must be instituted within six (6) months after the cause of action arose or be forever waived and barred. All actions shall be subject to the limitations set forth in paragraph 7 of this Agreement.\par +{\pntext\f0 3.\tab}The language in this Agreement shall be interpreted as to its fair meaning and not construed strictly for or against either party. The division of this Agreement into sections and the insertion of headings are for convenience of reference only and shall not affect the construction or interpretation of this Agreement.\par +{\pntext\f0 4.\tab}All legal proceedings arising out of or in connection with this Agreement shall be brought solely in Vancouver, British Columbia. You expressly submit to the exclusive jurisdiction of said courts and consent to extra-territorial service of process.\par +{\pntext\f0 5.\tab}Should any part of this Agreement be held invalid or unenforceable, that portion shall be construed consistent with applicable law and the remaining portions shall remain in full force and effect.\par +{\pntext\f0 6.\tab}Failure of Us to enforce any provision of this Agreement shall not be deemed a waiver of such provision nor of the right to enforce such provision.\par +{\pntext\f0 7.\tab}You agree to review this Agreement prior to downloading, copying, installing or using the Licenced Materials. This Agreement may be amended by Us from time to time without specific advance notice to You. The latest Agreement will be provided with updates to the Licenced Software, and You should review the Agreement prior to using the Licenced Materials.\par +{\pntext\f0 8.\tab}This Agreement, as modified from time to time as described above, and including the policies incorporated by reference, constitutes the entire and only agreement between You and Us and supersedes all prior or contemporaneous agreements, representations, warranties and understandings with respect to the Licenced Materials.\par +{\pntext\f0 9.\tab}To the extent that anything in or associated with the Licenced Materials is in conflict or inconsistent with this Agreement, this Agreement shall take precedence.\par + +\pard\widctlpar\sa160\sl252\slmult1\cf0\fs22\par +} + \ No newline at end of file diff --git a/Installer/build.sh b/Installer/build.sh new file mode 100755 index 0000000..482d42e --- /dev/null +++ b/Installer/build.sh @@ -0,0 +1,259 @@ +#!/bin/bash -e +set -x + +# +# Resolve repo root +# +cd "$(dirname "$0")" +cd .. +PROJECT_ROOT=$(pwd) +PLUGIN="Wavetable" +VENDOR="SocaLabs" +BUNDLE_BASE="com.socalabs.wavetable" + +if [ "$(uname)" = "Darwin" ]; then + PLATFORM="macOS" +elif [ "$(expr substr "$(uname -s)" 1 5)" = "Linux" ]; then + PLATFORM="linux" +else + PLATFORM="win" +fi + +VERSION=$(cat "$PROJECT_ROOT/VERSION") + +# +# Reset staging +# +rm -Rf "$PROJECT_ROOT/Installer/$PLATFORM/bin" +mkdir -p "$PROJECT_ROOT/Installer/$PLATFORM/bin" +rm -Rf "$PROJECT_ROOT/bin" +mkdir -p "$PROJECT_ROOT/bin" + +############################################################ +# macOS — pkgbuild + productbuild +############################################################ +if [ "$PLATFORM" = "macOS" ]; then + TEAM_ID="${TEAM_ID:-3FS7DJDG38}" + DEV_APP_ID="${DEV_APP_ID:-Developer ID Application: Roland Rabien (${TEAM_ID})}" + DEV_INST_ID="${DEV_INST_ID:-Developer ID Installer: Roland Rabien (${TEAM_ID})}" + + # + # Bootstrap a temp keychain from base64-encoded p12 secrets when running in CI. + # APPLICATION = base64 of Developer ID Application .p12 + # INSTALLER = base64 of Developer ID Installer .p12 + # + if [ -n "${APPLICATION:-}" ] && [ -n "${INSTALLER:-}" ]; then + KC_PASS="${KEYCHAIN_PASSWORD:-nr4aGPyz}" + P12_PASS="${P12_PASSWORD:-aym9PKWB}" + + security create-keychain -p "$KC_PASS" Keys.keychain || true + + echo "$APPLICATION" | base64 -D -o /tmp/Application.p12 + echo "$INSTALLER" | base64 -D -o /tmp/Installer.p12 + + security import /tmp/Application.p12 -t agg -k Keys.keychain -P "$P12_PASS" -A -T /usr/bin/codesign + security import /tmp/Installer.p12 -t agg -k Keys.keychain -P "$P12_PASS" -A -T /usr/bin/productsign + + security list-keychains -s Keys.keychain + security default-keychain -s Keys.keychain + security unlock-keychain -p "$KC_PASS" Keys.keychain + security set-keychain-settings -l -u -t 13600 Keys.keychain + security set-key-partition-list -S apple-tool:,apple: -s -k "$KC_PASS" Keys.keychain + fi + + ART_DIR="$PROJECT_ROOT/Builds/xcode/${PLUGIN}_artefacts/Release" + + cd "$PROJECT_ROOT" + cmake --preset xcode + cmake --build --preset xcode --config Release + + STAGE="$PROJECT_ROOT/Installer/macOS/bin/stage" + PKG_DIR="$PROJECT_ROOT/Installer/macOS/bin/pkgs" + rm -Rf "$STAGE" "$PKG_DIR" + mkdir -p "$STAGE/vst" "$STAGE/vst3" "$STAGE/au" "$STAGE/clap" + mkdir -p "$STAGE/resources/Library/Audio/Presets/$VENDOR/$PLUGIN" + mkdir -p "$PKG_DIR" + + cp -RL "$ART_DIR/VST/$PLUGIN.vst" "$STAGE/vst/" + cp -RL "$ART_DIR/VST3/$PLUGIN.vst3" "$STAGE/vst3/" + cp -RL "$ART_DIR/AU/$PLUGIN.component" "$STAGE/au/" + cp -RL "$ART_DIR/CLAP/$PLUGIN.clap" "$STAGE/clap/" + + # Resources component: factory Wavetables and Presets + cp -R "$PROJECT_ROOT/plugin/Resources/Presets" "$STAGE/resources/Library/Audio/Presets/$VENDOR/$PLUGIN/Presets" + cp -R "$PROJECT_ROOT/plugin/Resources/WavetablesFLAC" "$STAGE/resources/Library/Audio/Presets/$VENDOR/$PLUGIN/Wavetables" + find "$STAGE/resources" -name ".DS_Store" -delete + + if security find-identity -v -p codesigning | grep -q "$DEV_APP_ID"; then + codesign -s "$DEV_APP_ID" --options=runtime --timestamp --force -v "$STAGE/vst/$PLUGIN.vst" + codesign -s "$DEV_APP_ID" --options=runtime --timestamp --force -v "$STAGE/vst3/$PLUGIN.vst3" + codesign -s "$DEV_APP_ID" --options=runtime --timestamp --force -v "$STAGE/au/$PLUGIN.component" + codesign -s "$DEV_APP_ID" --options=runtime --timestamp --force -v "$STAGE/clap/$PLUGIN.clap" + else + echo "Skipping codesign — Developer ID Application not in keychain" + fi + + # Component packages + pkgbuild --root "$STAGE/vst" \ + --install-location "/Library/Audio/Plug-Ins/VST" \ + --identifier "${BUNDLE_BASE}.vst.pkg" \ + --version "$VERSION" \ + "$PKG_DIR/vst.pkg" + + pkgbuild --root "$STAGE/vst3" \ + --install-location "/Library/Audio/Plug-Ins/VST3" \ + --identifier "${BUNDLE_BASE}.vst3.pkg" \ + --version "$VERSION" \ + "$PKG_DIR/vst3.pkg" + + pkgbuild --root "$STAGE/au" \ + --install-location "/Library/Audio/Plug-Ins/Components" \ + --identifier "${BUNDLE_BASE}.au.pkg" \ + --version "$VERSION" \ + "$PKG_DIR/au.pkg" + + pkgbuild --root "$STAGE/clap" \ + --install-location "/Library/Audio/Plug-Ins/CLAP" \ + --identifier "${BUNDLE_BASE}.clap.pkg" \ + --version "$VERSION" \ + "$PKG_DIR/clap.pkg" + + pkgbuild --root "$STAGE/resources" \ + --install-location "/" \ + --identifier "${BUNDLE_BASE}.resources.pkg" \ + --version "$VERSION" \ + --scripts "$PROJECT_ROOT/Installer/macOS/scripts" \ + "$PKG_DIR/resources.pkg" + + # productbuild — combine into one signed installer + cp "$PROJECT_ROOT/Installer/EULA.rtf" "$PKG_DIR/EULA.rtf" + cp "$PROJECT_ROOT/Installer/macOS/welcome.txt" "$PKG_DIR/welcome.txt" + + PKG_OUT="$PROJECT_ROOT/Installer/macOS/bin/${PLUGIN}.pkg" + + productbuild --distribution "$PROJECT_ROOT/Installer/macOS/distribution.xml" \ + --package-path "$PKG_DIR" \ + --resources "$PKG_DIR" \ + --version "$VERSION" \ + "$PKG_OUT.unsigned" + + if security find-identity -v | grep -q "$DEV_INST_ID"; then + productsign --sign "$DEV_INST_ID" "$PKG_OUT.unsigned" "$PKG_OUT" + rm "$PKG_OUT.unsigned" + else + echo "Skipping productsign — Developer ID Installer not in keychain" + mv "$PKG_OUT.unsigned" "$PKG_OUT" + fi + + if [ -n "${APPLE_USER:-}" ] && [ -n "${APPLE_PASS:-}" ]; then + xcrun notarytool submit --verbose \ + --apple-id "$APPLE_USER" --password "$APPLE_PASS" --team-id "$TEAM_ID" \ + --wait --timeout 30m "$PKG_OUT" + xcrun stapler staple "$PKG_OUT" + else + echo "Skipping notarization — APPLE_USER / APPLE_PASS not set" + fi + + cp "$PKG_OUT" "$PROJECT_ROOT/bin/" + + # Symbols zip + cd "$ART_DIR" + zip -r "$PROJECT_ROOT/bin/Symbols_Mac.zip" \ + AU/$PLUGIN.component.dSYM \ + VST/$PLUGIN.vst.dSYM \ + VST3/$PLUGIN.vst3.dSYM \ + CLAP/$PLUGIN.clap.dSYM 2>/dev/null || true + +############################################################ +# Linux — cpack DEB (VST + VST3 + LV2 + CLAP + Resources) +############################################################ +elif [ "$PLATFORM" = "linux" ]; then + cd "$PROJECT_ROOT" + cmake --preset ninja-gcc + cmake --build --preset ninja-gcc --config Release + + cd "$PROJECT_ROOT/Builds/ninja-gcc" + cpack -G DEB -C Release + + cp "$PROJECT_ROOT/Builds/ninja-gcc/"*.deb "$PROJECT_ROOT/bin/" + +############################################################ +# Windows — Inno Setup + Azure Trusted Signing +############################################################ +else + cd "$PROJECT_ROOT" + cmake --preset vs + cmake --build --preset vs --config Release + + ART_DIR="$PROJECT_ROOT/Builds/vs/${PLUGIN}_artefacts/Release" + STAGE="$PROJECT_ROOT/Installer/win/bin" + + rm -Rf "$STAGE" + mkdir -p "$STAGE/VST" "$STAGE/VST3" "$STAGE/CLAP" + cp -R "$ART_DIR/VST/$PLUGIN.dll" "$STAGE/VST/" + cp -R "$ART_DIR/VST3/$PLUGIN.vst3" "$STAGE/VST3/" + cp -R "$ART_DIR/CLAP/$PLUGIN.clap" "$STAGE/CLAP/" + + # + # Sign binaries via Microsoft Trusted Signing. + # Required env: AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET. + # Cert is referenced via Installer/win/metadata.json. + # + uuid_re='^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$' + WIN_SIGN=0 + if [ -n "${AZURE_TENANT_ID:-}" ] || [ -n "${AZURE_CLIENT_ID:-}" ] || [ -n "${AZURE_CLIENT_SECRET:-}" ]; then + if [[ ! "${AZURE_TENANT_ID:-}" =~ $uuid_re ]]; then + echo "ERROR: AZURE_TENANT_ID is not a valid GUID — set the secret on the repo or unset all three to skip signing."; exit 1 + elif [[ ! "${AZURE_CLIENT_ID:-}" =~ $uuid_re ]]; then + echo "ERROR: AZURE_CLIENT_ID is not a valid GUID — set the secret on the repo or unset all three to skip signing."; exit 1 + elif [ -z "${AZURE_CLIENT_SECRET:-}" ]; then + echo "ERROR: AZURE_CLIENT_SECRET is empty — set the secret on the repo or unset all three to skip signing."; exit 1 + else + WIN_SIGN=1 + fi + fi + + if [ "$WIN_SIGN" = "1" ]; then + # Locate signtool — the version dir under Windows Kits varies by runner image. + SIGNTOOL=$(ls -1 "/c/Program Files (x86)/Windows Kits/10/bin/"*/x64/signtool.exe 2>/dev/null | sort | tail -1) + if [ -z "$SIGNTOOL" ]; then + echo "signtool.exe not found in Windows Kits"; exit 1 + fi + + TOOLS_DIR="$STAGE/_signingtools" + mkdir -p "$TOOLS_DIR" + nuget install Microsoft.Trusted.Signing.Client -Version 1.0.86 \ + -OutputDirectory "$TOOLS_DIR" -ExcludeVersion -NonInteractive + DLIB="$TOOLS_DIR/Microsoft.Trusted.Signing.Client/bin/x64/Azure.CodeSigning.Dlib.dll" + METADATA="$PROJECT_ROOT/Installer/win/metadata.json" + + sign_file () { + "$SIGNTOOL" sign -v -fd SHA256 \ + -tr "http://timestamp.acs.microsoft.com" -td SHA256 \ + -dlib "$DLIB" -dmdf "$METADATA" "$1" + } + + sign_file "$STAGE/VST/$PLUGIN.dll" + sign_file "$STAGE/VST3/$PLUGIN.vst3/Contents/x86_64-win/$PLUGIN.vst3" + sign_file "$STAGE/CLAP/$PLUGIN.clap" + else + echo "Skipping Windows binary signing — Azure secrets not set" + fi + + # Build installer .exe + cd "$PROJECT_ROOT/Installer/win" + ISCC="/c/Program Files (x86)/Inno Setup 6/ISCC.exe" + if [ ! -f "$ISCC" ]; then + ISCC="/c/Program Files/Inno Setup 6/ISCC.exe" + fi + "$ISCC" "$PROJECT_ROOT/Installer/win/${PLUGIN}.iss" + + EXE_OUT="$PROJECT_ROOT/Installer/win/bin/${PLUGIN}.exe" + + # Sign installer + if [ "$WIN_SIGN" = "1" ]; then + sign_file "$EXE_OUT" + fi + + cp "$EXE_OUT" "$PROJECT_ROOT/bin/" +fi diff --git a/Installer/eula.md b/Installer/eula.md new file mode 100644 index 0000000..887387e --- /dev/null +++ b/Installer/eula.md @@ -0,0 +1,70 @@ +# End-User Licence Agreement # + +READ THE FOLLOWING LICENCE AGREEMENT CAREFULLY! IT CONTAINS VERY IMPORTANT INFORMATION ABOUT YOUR RIGHTS AND OBLIGATIONS, AS WELL AS LIMITATIONS AND EXCLUSIONS THAT MAY APPLY TO YOU. THIS DOCUMENT CONTAINS A DISPUTE RESOLUTION CLAUSE. BY CLICKING ON THE "ACCEPT" BUTTON, YOU ARE CONSENTING TO BE BOUND BY AND ARE BECOMING A PARTY TO THIS AGREEMENT. IF YOU DO NOT AGREE TO ALL OF THE TERMS OF THIS AGREEMENT, CLICK THE "DO NOT ACCEPT" BUTTON OR LEAVE THE WEBSITE. + +## 1. Definitions ## + +1. "We", "Our" and "Us" means SocaLabs. +2. "You" and "Your" means the person or entity who is being licenced to use the Licenced Software. +3. "Agreement" means this Software Licence Agreement between Us and You; +4. "Licenced Materials" means the Licenced Software and the Licenced Library; +5. "Licenced Software" means certain commercial software products being provided to You under this Agreement, including executable program modules thereof, as well as related documentation, and computer readable media; +6. "Sub-Licenced Software" means certain third party owned software components being provided under this Agreement that are required to properly enable or operate the Licenced Software; and +7. "Licenced Library" means any audio waveforms, such as melodies, music, instrument recordings, or other audible information, provided by Us for use with the Licenced Software. + +## 2. Software Licence, SamplePack Licence, Rights and Restrictions ## + +1. Subject to the provisions contained in this Agreement, We hereby grant to You a personal, revocable, non-exclusive licence (the "Licence") to access, read, use and download one copy of the Licenced Materials solely for the purposes provided under paragraph 3. This is not a transfer of title. +2. Under the Licence you may not and shall not, without Our express written authorization: + 1. own title, or transfer title to the Licenced Materials to another party; + 2. transfer the Licence; + 3. remove any copyright or other proprietary notations from the Licenced Materials; + 4. distribute, or sublicense or otherwise provide copies of or access to the Licenced Materials to any third party; + 5. pledge, hypothecate, alienate or otherwise encumber the Licenced Materials to any third party; + 6. create derivative works of, reverse engineer, decompile, disassemble, adapt, translate, transmit, arrange, modify, copy, bundle, sell, sublicense, export, merge, transfer, adapt, loan, rent, lease, assign, share, outsource, host, publish, make available to any person or otherwise use, either directly or indirectly, the Licenced Materials; + 7. permit, allow or do anything that would infringe or otherwise prejudice the proprietary rights of Us or Our licensors; + 8. circumvent, remove, alter or otherwise impair any technological protection measures, digital rights management information, or other copy-protection mechanisms used in or by the Licenced Materials; or + 9. include, use, re-sample, mix, process, isolate or embed any content from the Licenced Library in any virtual instrument, audio sampling system, audio playback system or library of any kind, other than those provided or authorized by Us. +3. We are not obligated to provide any authorization referred to in paragraph 2(2), We reserve the right to charge a fee for the grant of such authorization, and We may cancel such authorization at Our sole and unfettered discretion by providing notice to You of such cancellation. +4. The restrictions set out in this Agreement, including the restrictions listed at paragraph 2(2), shall not apply to the limited extent the restrictions are prohibited by applicable law. +5. We will have the right to inspect and enforce the restrictions and covenants contained in this Agreement at Your sole expense, and You hereby agree to promptly notify Us of any known violations of such restrictions. +6. You agree to protect the Licenced Materials from unauthorized use, reproduction, distribution or publication in electronic or physical form. +7. Upon execution of this Agreement, We will: + 1. permit You to download a copy of the Licenced Materials for Your use under this Agreement; and + 2. permit You to download updates to the Licenced Software as We consider needed. We are not obligated to provide updates for the Licenced Software. + +## 3. Usage Conditions ## +1. You are authorized to use the Licenced Materials only for personal use. +2. You may not rent out to others equipment on which the Licenced Software is installed or by which the Licenced Library is accessible without obtaining a written rental licence from Us that expressly authorizes such use. We reserve the right to charge a fee for a rental licence. +3. You may install the Licenced Software on as many devices as required for Your personal use provided such devices are used only by You. + +## 4. Ownership and Intellectual Property Rights ## +1. All copyright in the Licenced Software, including any documentation, media, packaging and illustrations, but excluding the Sublicenced Software, is owned by Us. You do not acquire title to any copyright in the Licenced Materials or Sublicenced Software under this Agreement. +2. Certain logos, product names and trade-marks owned by Us or by third parties may be contained within the Licenced Materials or in documentation, media, packaging and illustrations relating to the Licensed Software. You do not acquire title to any such logos, product names or trade-marks under this Agreement. + +## 5. Termination ## +1. This Agreement is effective until terminated by Us, with or without cause, in Our sole and unfettered discretion. We may terminate this Agreement without notice to You if You fail to comply with any of its terms. Any such termination by Us shall be in addition to and without prejudice to such rights and remedies as may be available to Us, including injunction and other equitable remedies. +2. The disclaimers, limitations on liability, ownership, termination, interpretation, Your warranty and the indemnity provisions of this Agreement shall survive the termination or expiry of this Agreement. +3. This Agreement will automatically terminate if You violate or assist in the violation of any of the restrictions of paragraph 2(2). +4. On the termination of this Agreement, you must destroy any copies of the Licenced Materials in your possession whether in electronic or printed format. + +## 6. Indemnification ## +You agree to indemnify, defend and hold Us and Our partners, lawyers, staff, affiliates, successors and assigns (collectively, "Affiliated Parties") harmless from any liability, loss, claim and expense, including reasonable legal fees, related to Your violation of this Agreement or use of the Licenced Materials. + +## 7. Disclaimer and Limitation of Liability ## +1. THE LICENCED MATERIALS ARE PROVIDED "AS IS", "AS AVAILABLE" AND ALL WARRANTIES, EXPRESS OR IMPLIED, ARE DISCLAIMED (INCLUDING BUT NOT LIMITED TO THE DISCLAIMER OF ANY IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE). THE LICENCED MATERIALS MAY CONTAIN BUGS, ERRORS, PROBLEMS OR OTHER LIMITATIONS. WE AND OUR AFFILIATED PARTIES HAVE NO LIABILITY WHATSOEVER FOR YOUR USE OF ANY OF THE MATERIALS. IN PARTICULAR, BUT NOT AS A LIMITATION THEREOF, WE AND THE AFFILIATED PARTIES ARE NOT LIABLE FOR ANY INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING DAMAGES FOR LOSS OF BUSINESS, LOSS OF PROFITS, LITIGATION OR THE LIKE), WHETHER BASED ON BREACH OF CONTRACT, BREACH OF WARRANTY, TORT (INCLUDING NEGLIGENCE), PRODUCT LIABILITY OR OTHERWISE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THE NEGATION OF DAMAGES SET FORTH ABOVE ARE FUNDAMENTAL ELEMENTS OF THE BASIS OF THE BARGAIN BETWEEN YOU AND US. THE LICENCED MATERIALS WOULD NOT BE PROVIDED WITHOUT SUCH LIMITATIONS. +2. All responsibility or liability for any damages caused by the Licenced Materials, including, without limitation, damages caused by computer viruses or other malicious code contained within the Licenced Software is disclaimed. + +## 8. Use of Information ## +We reserve the right, and You authorize Us, to the use and assignment of all information regarding Your use of the Licenced Materials and all information provided by You in any manner consistent with Our Privacy Policy. + +## 9. Miscellaneous ## +1. This Agreement shall be treated as though it were executed and performed in the province of British Columbia, Canada, and shall be governed by and construed in accordance with the laws of the province of British Columbia (without regard to conflict of law principles). +2. Any of Your causes of action with respect to the Licenced Materials must be instituted within six (6) months after the cause of action arose or be forever waived and barred. All actions shall be subject to the limitations set forth in paragraph 7 of this Agreement. +3. The language in this Agreement shall be interpreted as to its fair meaning and not construed strictly for or against either party. The division of this Agreement into sections and the insertion of headings are for convenience of reference only and shall not affect the construction or interpretation of this Agreement. +4. All legal proceedings arising out of or in connection with this Agreement shall be brought solely in Vancouver, British Columbia. You expressly submit to the exclusive jurisdiction of said courts and consent to extra-territorial service of process. +5. Should any part of this Agreement be held invalid or unenforceable, that portion shall be construed consistent with applicable law and the remaining portions shall remain in full force and effect. +6. Failure of Us to enforce any provision of this Agreement shall not be deemed a waiver of such provision nor of the right to enforce such provision. +7. You agree to review this Agreement prior to downloading, copying, installing or using the Licenced Materials. This Agreement may be amended by Us from time to time without specific advance notice to You. The latest Agreement will be provided with updates to the Licenced Software, and You should review the Agreement prior to using the Licenced Materials. +8. This Agreement, as modified from time to time as described above, and including the policies incorporated by reference, constitutes the entire and only agreement between You and Us and supersedes all prior or contemporaneous agreements, representations, warranties and understandings with respect to the Licenced Materials. +9. To the extent that anything in or associated with the Licenced Materials is in conflict or inconsistent with this Agreement, this Agreement shall take precedence. diff --git a/Installer/macOS/distribution.xml b/Installer/macOS/distribution.xml new file mode 100644 index 0000000..63aa3fd --- /dev/null +++ b/Installer/macOS/distribution.xml @@ -0,0 +1,60 @@ + + + Wavetable + com.socalabs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + vst.pkg + vst3.pkg + au.pkg + clap.pkg + resources.pkg + diff --git a/Installer/macOS/scripts/postinstall b/Installer/macOS/scripts/postinstall new file mode 100755 index 0000000..cb438fc --- /dev/null +++ b/Installer/macOS/scripts/postinstall @@ -0,0 +1,6 @@ +#!/bin/sh +# Force the AU host registrar to rescan so the freshly installed Component is +# visible without a logout/reboot. +killall -9 AudioComponentRegistrar >/dev/null 2>&1 + +exit 0 diff --git a/Installer/macOS/scripts/preinstall b/Installer/macOS/scripts/preinstall new file mode 100755 index 0000000..bbaaf87 --- /dev/null +++ b/Installer/macOS/scripts/preinstall @@ -0,0 +1,14 @@ +#!/bin/sh +# Remove any prior install of Wavetable so the new bundle takes its place cleanly. + +rm -Rf "/Library/Audio/Plug-Ins/VST/Wavetable.vst" +rm -Rf "/Library/Audio/Plug-Ins/VST3/Wavetable.vst3" +rm -Rf "/Library/Audio/Plug-Ins/Components/Wavetable.component" +rm -Rf "/Library/Audio/Plug-Ins/CLAP/Wavetable.clap" + +# Wipe the previous shipped factory content so removed wavetables/presets from +# the old version don't linger. User content lives under ~/Library and is left alone. +rm -Rf "/Library/Audio/Presets/SocaLabs/Wavetable/Presets" +rm -Rf "/Library/Audio/Presets/SocaLabs/Wavetable/Wavetables" + +exit 0 diff --git a/Installer/macOS/welcome.txt b/Installer/macOS/welcome.txt new file mode 100644 index 0000000..1d7254a --- /dev/null +++ b/Installer/macOS/welcome.txt @@ -0,0 +1,17 @@ +Wavetable by SocaLabs +===================== + +This installer places the Wavetable plug-in in your system audio plug-in +folders along with the factory wavetable bank and factory presets. + +Components: + • VST /Library/Audio/Plug-Ins/VST + • VST3 /Library/Audio/Plug-Ins/VST3 + • Audio Unit /Library/Audio/Plug-Ins/Components + • CLAP /Library/Audio/Plug-Ins/CLAP + • Factory content /Library/Audio/Presets/SocaLabs/Wavetable + +User-saved presets live under + ~/Library/Audio/Presets/SocaLabs/Wavetable + +and are not touched by this installer. diff --git a/Installer/win/Wavetable.iss b/Installer/win/Wavetable.iss new file mode 100644 index 0000000..9978fe8 --- /dev/null +++ b/Installer/win/Wavetable.iss @@ -0,0 +1,75 @@ +; Wavetable installer (Inno Setup) + +#define MyAppName "Wavetable" +#define MyAppCompany "SocaLabs" +#define MyAppPublisher "SocaLabs" +#define MyAppCopyright "2026 SocaLabs" +#define MyAppURL "https://socalabs.com/" +#define MyAppVersion GetStringFileInfo("bin\VST3\Wavetable.vst3\Contents\x86_64-win\Wavetable.vst3", "ProductVersion") +#define MyDefaultDirName "{commoncf64}\VST3" + +[Setup] +AppID={{FB36B27A-9328-4438-8612-3B4E73021845} +AppName={#MyAppCompany} {#MyAppName} {#MyAppVersion} +AppVerName={#MyAppCompany} {#MyAppName} {#MyAppVersion} +AppVersion={#MyAppVersion} +AppCopyright={#MyAppCopyright} +AppPublisher={#MyAppPublisher} +AppPublisherURL={#MyAppURL} +AppSupportURL={#MyAppURL} +AppUpdatesURL={#MyAppURL} +DefaultDirName={#MyDefaultDirName} +DisableProgramGroupPage=yes +OutputDir=.\bin +OutputBaseFilename=Wavetable +Compression=lzma/ultra +SolidCompression=true +ShowLanguageDialog=auto +LicenseFile=..\EULA.rtf +InternalCompressLevel=ultra +MinVersion=0,6.1.7600 +FlatComponentsList=false +AppendDefaultDirName=false +AlwaysShowDirOnReadyPage=yes +DirExistsWarning=no +DisableDirPage=yes +DisableWelcomePage=no +DisableReadyPage=no +DisableReadyMemo=no +ArchitecturesAllowed=x64compatible +ArchitecturesInstallIn64BitMode=x64compatible +VersionInfoVersion={#MyAppVersion} +VersionInfoCompany={#MyAppPublisher} +VersionInfoCopyright={#MyAppCopyright} +VersionInfoProductName={#MyAppCompany} {#MyAppName} {#MyAppVersion} (64-bit) +VersionInfoProductVersion={#MyAppVersion} +VersionInfoProductTextVersion={#MyAppVersion} +UsePreviousGroup=False +Uninstallable=no +PrivilegesRequired=admin + +[Languages] +Name: english; MessagesFile: compiler:Default.isl + +[Components] +Name: "vst"; Description: "VST plug-in"; Types: full custom; Flags: checkablealone +Name: "vst3"; Description: "VST3 plug-in"; Types: full custom; Flags: checkablealone +Name: "clap"; Description: "CLAP plug-in"; Types: full custom; Flags: checkablealone +Name: "resources"; Description: "Factory wavetables and presets"; Types: full custom; Flags: fixed + +[InstallDelete] +Type: files; Name: "{commoncf64}\VST\Wavetable.dll"; Components: vst +Type: filesandordirs; Name: "{commoncf64}\VST3\Wavetable.vst3"; Components: vst3 +Type: files; Name: "{commoncf64}\CLAP\Wavetable.clap"; Components: clap +Type: filesandordirs; Name: "{commonappdata}\SocaLabs\Wavetable\Presets"; Components: resources +Type: filesandordirs; Name: "{commonappdata}\SocaLabs\Wavetable\Wavetables"; Components: resources + +[Files] +; Plug-in formats +Source: "bin\VST\Wavetable.dll"; DestDir: "{commoncf64}\VST"; Flags: ignoreversion overwritereadonly; Components: vst +Source: "bin\VST3\Wavetable.vst3\*"; DestDir: "{commoncf64}\VST3\Wavetable.vst3\"; Flags: ignoreversion overwritereadonly recursesubdirs; Components: vst3 +Source: "bin\CLAP\Wavetable.clap"; DestDir: "{commoncf64}\CLAP"; Flags: ignoreversion overwritereadonly; Components: clap + +; Factory content (Wavetables, Presets) → C:\ProgramData\SocaLabs\Wavetable\ +Source: "..\..\plugin\Resources\Presets\*.xml"; DestDir: "{commonappdata}\SocaLabs\Wavetable\Presets\"; Flags: ignoreversion; Components: resources +Source: "..\..\plugin\Resources\WavetablesFLAC\*.wt2048"; DestDir: "{commonappdata}\SocaLabs\Wavetable\Wavetables\"; Flags: ignoreversion recursesubdirs createallsubdirs; Components: resources diff --git a/Installer/win/metadata.json b/Installer/win/metadata.json new file mode 100644 index 0000000..ff5df89 --- /dev/null +++ b/Installer/win/metadata.json @@ -0,0 +1,5 @@ +{ + "Endpoint": "https://wus.codesigning.azure.net/", + "CodeSigningAccountName": "Signing2025", + "CertificateProfileName": "rabien" +} diff --git a/ci/build.sh b/ci/build.sh deleted file mode 100755 index 0d560c0..0000000 --- a/ci/build.sh +++ /dev/null @@ -1,148 +0,0 @@ -#!/bin/bash -e -set +x - -PLUGIN="Wavetable" - -# linux specific stiff -if [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then - sudo apt-get update - sudo apt-get install clang git ninja-build ladspa-sdk freeglut3-dev g++ libasound2-dev libcurl4-openssl-dev libfreetype6-dev libjack-jackd2-dev libx11-dev libxcomposite-dev libxcursor-dev libxinerama-dev libxrandr-dev mesa-common-dev webkit2gtk-4.0 juce-tools xvfb -fi - -# mac specific stuff -if [ "$(uname)" == "Darwin" ]; then - # Create a temp keychain - if [ -n "$GITHUB_ACTIONS" ]; then - if [ -n "$APPLICATION" ]; then - echo "Create a keychain" - security create-keychain -p nr4aGPyz Keys.keychain - - echo $APPLICATION | base64 -D -o /tmp/Application.p12 - echo $INSTALLER | base64 -D -o /tmp/Installer.p12 - - security import /tmp/Application.p12 -t agg -k Keys.keychain -P aym9PKWB -A -T /usr/bin/codesign - security import /tmp/Installer.p12 -t agg -k Keys.keychain -P aym9PKWB -A -T /usr/bin/codesign - - security list-keychains -s Keys.keychain - security default-keychain -s Keys.keychain - security unlock-keychain -p nr4aGPyz Keys.keychain - security set-keychain-settings -l -u -t 13600 Keys.keychain - security set-key-partition-list -S apple-tool:,apple: -s -k nr4aGPyz Keys.keychain - fi - DEV_APP_ID="Developer ID Application: Roland Rabien (3FS7DJDG38)" - DEV_INST_ID="Developer ID Installer: Roland Rabien (3FS7DJDG38)" - fi -fi - -ROOT=$(cd "$(dirname "$0")/.."; pwd) -cd "$ROOT" -echo "$ROOT" - -BRANCH=${GITHUB_REF##*/} -echo "$BRANCH" - -cd "$ROOT/ci" -rm -Rf bin -mkdir bin - -# Build mac version -if [ "$(uname)" == "Darwin" ]; then - cd "$ROOT" - cmake --preset xcode - cmake --build --preset xcode --config Release - - mkdir -p "$ROOT/ci/bin/au" - mkdir -p "$ROOT/ci/bin/vst" - mkdir -p "$ROOT/ci/bin/vst3" - mkdir -p "$ROOT/ci/bin/clap" - - cp -R "$ROOT/Builds/xcode/${PLUGIN}_artefacts/Release/AU/$PLUGIN.component" "$ROOT/ci/bin/au" - cp -R "$ROOT/Builds/xcode/${PLUGIN}_artefacts/Release/VST/$PLUGIN.vst" "$ROOT/ci/bin/vst" - cp -R "$ROOT/Builds/xcode/${PLUGIN}_artefacts/Release/VST3/$PLUGIN.vst3" "$ROOT/ci/bin/vst3" - cp -R "$ROOT/Builds/xcode/${PLUGIN}_artefacts/Release/CLAP/$PLUGIN.clap" "$ROOT/ci/bin/clap" - - cd "$ROOT/ci/bin" - if [ -n "$APPLICATION" ]; then - codesign -s "$DEV_APP_ID" -v vst/$PLUGIN.vst --options=runtime --timestamp --force - codesign -s "$DEV_APP_ID" -v vst3/$PLUGIN.vst3 --options=runtime --timestamp --force - codesign -s "$DEV_APP_ID" -v au/$PLUGIN.component --options=runtime --timestamp --force - codesign -s "$DEV_APP_ID" -v clap/$PLUGIN.clap --options=runtime --timestamp --force - else - echo "Not signing" - fi - - # Notarize - cd "$ROOT/ci/bin" - - if [[ -n "$APPLE_USER" ]]; then - zip -r ${PLUGIN}_Mac.zip vst/$PLUGIN.vst vst3/$PLUGIN.vst3 au/$PLUGIN.component clap/$PLUGIN.clap - xcrun notarytool submit --verbose --apple-id "$APPLE_USER" --password "$APPLE_PASS" --team-id "3FS7DJDG38" --wait --timeout 30m ${PLUGIN}_Mac.zip - - rm ${PLUGIN}_Mac.zip - xcrun stapler staple vst/$PLUGIN.vst - xcrun stapler staple vst3/$PLUGIN.vst3 - xcrun stapler staple au/$PLUGIN.component - xcrun stapler staple clap/$PLUGIN.clap - else - echo "Not notarizing" - fi - - zip -r ${PLUGIN}_Mac.zip vst/$PLUGIN.vst vst3/$PLUGIN.vst3 au/$PLUGIN.component clap/$PLUGIN.clap - - if [ "$BRANCH" = "release" ]; then - curl -F "files=@${PLUGIN}_Mac.zip" "https://socalabs.com/files/set.php?key=$APIKEY" - fi -# Build linux version -elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then - cd "$ROOT" - - cmake --preset ninja-gcc - cmake --build --preset ninja-gcc --config Release - - mkdir -p "$ROOT/ci/bin/lv2" - mkdir -p "$ROOT/ci/bin/vst" - mkdir -p "$ROOT/ci/bin/vst3" - mkdir -p "$ROOT/ci/bin/clap" - - cp -R "$ROOT/Builds/ninja-gcc/${PLUGIN}_artefacts/Release/LV2/$PLUGIN.lv2" "$ROOT/ci/bin/lv2" - cp -R "$ROOT/Builds/ninja-gcc/${PLUGIN}_artefacts/Release/VST/lib$PLUGIN.so" "$ROOT/ci/bin/vst/$PLUGIN.so" - cp -R "$ROOT/Builds/ninja-gcc/${PLUGIN}_artefacts/Release/VST3/$PLUGIN.vst3" "$ROOT/ci/bin/vst3" - cp -R "$ROOT/Builds/ninja-gcc/${PLUGIN}_artefacts/Release/CLAP/$PLUGIN.clap" "$ROOT/ci/bin/clap" - - cd "$ROOT/ci/bin" - - # Strip debug symbols - strip vst/$PLUGIN.so - strip vst3/$PLUGIN.vst3/Contents/x86_64-linux/$PLUGIN.so - strip lv2/$PLUGIN.lv2/lib$PLUGIN.so - strip clap/$PLUGIN.clap - - # Upload - cd "$ROOT/ci/bin" - zip -r ${PLUGIN}_Linux.zip vst/$PLUGIN.so vst3/$PLUGIN.vst3 lv2/$PLUGIN.lv2 clap/$PLUGIN.clap - - if [ "$BRANCH" = "release" ]; then - curl -F "files=@${PLUGIN}_Linux.zip" "https://socalabs.com/files/set.php?key=$APIKEY" - fi -# Build Win version -elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then - cd "$ROOT" - - cmake --preset vs - cmake --build --preset vs --config Release - - mkdir -p "$ROOT/ci/bin/vst" - mkdir -p "$ROOT/ci/bin/vst3" - mkdir -p "$ROOT/ci/bin/clap" - - cp -R "$ROOT/Builds/vs/${PLUGIN}_artefacts/Release/VST/$PLUGIN.dll" "$ROOT/ci/bin/vst" - cp -R "$ROOT/Builds/vs/${PLUGIN}_artefacts/Release/VST3/$PLUGIN.vst3" "$ROOT/ci/bin/vst3" - cp -R "$ROOT/Builds/vs/${PLUGIN}_artefacts/Release/CLAP/$PLUGIN.clap" "$ROOT/ci/bin/clap" - - cd "$ROOT/ci/bin" - 7z a ${PLUGIN}_Win.zip vst/$PLUGIN.dll vst3/$PLUGIN.vst3 clap/$PLUGIN.clap - - if [ "$BRANCH" = "release" ]; then - curl -F "files=@${PLUGIN}_Win.zip" "https://socalabs.com/files/set.php?key=$APIKEY" - fi -fi diff --git a/ci/config_cmake.sh b/ci/config_cmake.sh deleted file mode 100755 index 233babd..0000000 --- a/ci/config_cmake.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -e - -ROOT=$(cd "$(dirname "$0")/.."; pwd) -cd "$ROOT" - -export PATH=$PATH:"/c/Program Files/CMake/bin" - -if [ "$(uname)" == "Darwin" ]; then - TOOLCHAIN="xcode" -elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then - TOOLCHAIN="ninja-gcc" -elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then - TOOLCHAIN="vs" -fi - -cmake --preset $TOOLCHAIN -D BUILD_EXTRAS=OFF -D JUCE_COPY_PLUGIN_AFTER_BUILD=ON diff --git a/ci/upload.sh b/ci/upload.sh deleted file mode 100755 index 986a3cd..0000000 --- a/ci/upload.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -e -set +x - -PLUGIN="Wavetable" - -ROOT=$(cd "$(dirname "$0")/.."; pwd) -cd "$ROOT" -echo "$ROOT" - -# Upload mac version -if [ "$(uname)" == "Darwin" ]; then - cd "$ROOT/ci/bin" - curl -F "files=@${PLUGIN}_Mac.zip" "https://socalabs.com/files/set.php?key=$APIKEY" -fi - -# Upload linux version -if [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then - cd "$ROOT/ci/bin" - curl -F "files=@${PLUGIN}_Linux.zip" "https://socalabs.com/files/set.php?key=$APIKEY" -fi - -# Upload Win version -if [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then - cd "$ROOT/ci/bin" - curl -F "files=@${PLUGIN}_Win.zip" "https://socalabs.com/files/set.php?key=$APIKEY" -fi diff --git a/modules/gin b/modules/gin index f26a6a3..7781d8d 160000 --- a/modules/gin +++ b/modules/gin @@ -1 +1 @@ -Subproject commit f26a6a3b8dc96c1de9b6011390ccc80308c85126 +Subproject commit 7781d8dafb59e6e4b7837ff8acb113fcae7291ea diff --git a/plugin/Source/Panels.h b/plugin/Source/Panels.h index 30c91ab..65bd3c0 100644 --- a/plugin/Source/Panels.h +++ b/plugin/Source/Panels.h @@ -143,12 +143,7 @@ public: } else if (e.originalComponent == &h && e.mouseWasClicked() && e.x >= prevButton.getRight() && e.x <= nextButton.getX()) { - juce::StringArray tables; - for (auto i = 0; i < BinaryData::namedResourceListSize; i++) - if (juce::String (BinaryData::originalFilenames[i]).endsWith (".wt2048")) - tables.add (juce::String (BinaryData::originalFilenames[i]).upToLastOccurrenceOf (".wt2048", false, false)); - - tables.sortNatural(); + auto tables = proc.getWavetableNames(); std::map menus; diff --git a/plugin/Source/PluginProcessor.cpp b/plugin/Source/PluginProcessor.cpp index 5432539..27bead0 100644 --- a/plugin/Source/PluginProcessor.cpp +++ b/plugin/Source/PluginProcessor.cpp @@ -507,6 +507,49 @@ void extractWavetables() } #endif +//============================================================================== +namespace +{ + juce::File userResourceRoot() + { + #if JUCE_MAC + return juce::File::getSpecialLocation (juce::File::userHomeDirectory) + .getChildFile ("Library/Audio/Presets/SocaLabs/Wavetable"); + #elif JUCE_WINDOWS + return juce::File::getSpecialLocation (juce::File::userApplicationDataDirectory) + .getChildFile ("SocaLabs/Wavetable"); + #else + return juce::File::getSpecialLocation (juce::File::userApplicationDataDirectory) + .getChildFile ("SocaLabs/Wavetable"); + #endif + } + + juce::File systemResourceRoot() + { + #if JUCE_MAC + return juce::File ("/Library/Audio/Presets/SocaLabs/Wavetable"); + #elif JUCE_WINDOWS + return juce::File::getSpecialLocation (juce::File::commonApplicationDataDirectory) + .getChildFile ("SocaLabs/Wavetable"); + #else + return juce::File ("/usr/share/SocaLabs/Wavetable"); + #endif + } + + // Pre-installer location used when factory presets were embedded and gin's + // default getProgramDirectory() chose a "programs" folder under devId. + juce::File legacyUserProgramDirectory() + { + #if JUCE_MAC + return juce::File::getSpecialLocation (juce::File::userApplicationDataDirectory) + .getChildFile ("Application Support/com.socalabs/Wavetable/programs"); + #else + return juce::File::getSpecialLocation (juce::File::userApplicationDataDirectory) + .getChildFile ("com.socalabs/Wavetable/programs"); + #endif + } +} + //============================================================================== static gin::ProcessorOptions createProcessorOptions() { @@ -520,12 +563,20 @@ WavetableAudioProcessor::WavetableAudioProcessor() fireAmp (FXBaseCallback ([this] { return gin::Processor::getSampleRate(); })), grindAmp (FXBaseCallback ([this] { return gin::Processor::getSampleRate(); })) { - { - auto sz = 0; - for (auto i = 0; i < BinaryData::namedResourceListSize; i++) - if (juce::String (BinaryData::originalFilenames[i]).endsWith (".xml")) - if (auto data = BinaryData::getNamedResource (BinaryData::namedResourceList[i], sz)) - extractProgram (BinaryData::originalFilenames[i], data, sz); + // One-time migration of any user presets from the pre-installer location. + // Factory presets now live in systemResourceRoot()/Presets and are surfaced + // via getFactoryProgramDirectories(). User saves go to userResourceRoot()/Presets. + { + auto oldDir = legacyUserProgramDirectory(); + auto newDir = userResourceRoot().getChildFile ("Presets"); + if (oldDir.isDirectory() + && newDir.findChildFiles (juce::File::findFiles, false, "*.xml").isEmpty()) + { + if (! newDir.isDirectory()) + newDir.createDirectory(); + for (auto f : oldDir.findChildFiles (juce::File::findFiles, false, "*.xml")) + f.copyFileTo (newDir.getChildFile (f.getFileName())); + } } mtsClient = MTS_RegisterClient(); @@ -592,14 +643,12 @@ void WavetableAudioProcessor::reloadWavetables() { auto loadMemory = [&] (const juce::String& name) -> juce::MemoryBlock { - for (auto i = 0; i < BinaryData::namedResourceListSize; i++) + auto file = systemResourceRoot().getChildFile ("Wavetables").getChildFile (name + ".wt2048"); + if (file.existsAsFile()) { - if ((name + ".wt2048").equalsIgnoreCase (BinaryData::originalFilenames[i])) - { - int sz = 0; - if (auto data = BinaryData::getNamedResource (BinaryData::namedResourceList[i], sz)) - return juce::MemoryBlock (data, sz); - } + juce::MemoryBlock mb; + if (file.loadFileAsData (mb)) + return mb; } return {}; }; @@ -651,12 +700,7 @@ void WavetableAudioProcessor::incWavetable (int osc, int delta) else userTable2.reset(); - juce::StringArray tables; - for (auto i = 0; i < BinaryData::namedResourceListSize; i++) - if (juce::String (BinaryData::originalFilenames[i]).endsWith (".wt2048")) - tables.add (juce::String (BinaryData::originalFilenames[i]).upToLastOccurrenceOf (".wt2048", false, false)); - - tables.sortNatural(); + auto tables = getWavetableNames(); auto idx = tables.indexOf (table.toString()); @@ -725,6 +769,34 @@ void WavetableAudioProcessor::updateState() state.setProperty ("wt2Data", userTable2.toBase64Encoding(), nullptr); } +//============================================================================== +juce::File WavetableAudioProcessor::getProgramDirectory() +{ + auto dir = userResourceRoot().getChildFile ("Presets"); + + if (! dir.isDirectory()) + dir.createDirectory(); + + return dir; +} + +juce::Array WavetableAudioProcessor::getFactoryProgramDirectories() +{ + return { systemResourceRoot().getChildFile ("Presets") }; +} + +juce::StringArray WavetableAudioProcessor::getWavetableNames() const +{ + juce::StringArray tables; + auto wtDir = systemResourceRoot().getChildFile ("Wavetables"); + if (wtDir.isDirectory()) + for (auto f : wtDir.findChildFiles (juce::File::findFiles, false, "*.wt2048")) + tables.add (f.getFileNameWithoutExtension()); + + tables.sortNatural(); + return tables; +} + //============================================================================== void WavetableAudioProcessor::setupModMatrix() { diff --git a/plugin/Source/PluginProcessor.h b/plugin/Source/PluginProcessor.h index f23d7a6..b96eb51 100644 --- a/plugin/Source/PluginProcessor.h +++ b/plugin/Source/PluginProcessor.h @@ -24,6 +24,9 @@ public: bool supportsMPE() const override { return true; } + juce::File getProgramDirectory() override; + juce::Array getFactoryProgramDirectories() override; + void stateUpdated() override; void updateState() override; @@ -52,6 +55,7 @@ public: void reloadWavetables(); void incWavetable (int osc, int delta); bool loadUserWavetable (int osc, const juce::File& f, int sz); + juce::StringArray getWavetableNames() const; void applyEffects (juce::AudioSampleBuffer& buffer); void applyEffect (juce::AudioSampleBuffer& buffer, int fxId); diff --git a/release.sh b/release.sh index 1b72bee..370c85c 100755 --- a/release.sh +++ b/release.sh @@ -23,4 +23,19 @@ fi echo "$NOTES" > /tmp/release_notes.txt -gh release create "$VER" --title "$VER" -F /tmp/release_notes.txt ./Binaries\ Linux/*.zip ./Binaries\ Windows/*.zip ./Binaries\ macOS/*.zip +ASSETS=( + "./Binaries Linux"/*.deb + "./Binaries Windows"/*.exe + "./Binaries macOS"/*.pkg +) +if [ -f "./Binaries macOS/Symbols_Mac.zip" ]; then + ASSETS+=("./Binaries macOS/Symbols_Mac.zip") +fi + +gh release create "$VER" --title "$VER" -F /tmp/release_notes.txt "${ASSETS[@]}" + +for f in "./Binaries Linux"/*.deb \ + "./Binaries Windows"/*.exe \ + "./Binaries macOS"/*.pkg; do + curl -F "files=@${f}" "https://socalabs.com/files/set.php?key=$APIKEY" +done