--- # ============================================================================ # life-church/playbooks/thinkcenter-reaper.yml # Install REAPER DAW (7.79) system-wide on thinkcenter, plus the SWS/S&M # extension and ReaPack into the login user's UserPlugins folder. # thinkcenter ONLY. Idempotent. become password from Infisical /lc-hosts. # # - REAPER: tarball from reaper.fm -> install-reaper.sh --quiet into /opt. # - SWS 2.14.0.4 (stable) + ReaPack 1.2.6: single .so files dropped into # ~/.config/REAPER/UserPlugins/ (per-user, owned by the login user). # - Audio: ensures pipewire-jack so REAPER's JACK backend maps to PipeWire, # and adds the user to the `audio` group for RT scheduling. # # UNDO: # sudo /opt/REAPER/uninstall-reaper.sh (or: sudo rm -rf /opt/REAPER /usr/local/bin/reaper) # rm -f ~/.config/REAPER/UserPlugins/reaper_sws-x86_64.so # rm -f ~/.config/REAPER/UserPlugins/reaper_reapack-x86_64.so # ============================================================================ # --- Play 1 (localhost): fetch thinkcenter sudo pass + user from Infisical --- - name: "thinkcenter REAPER — resolve become password from Infisical" hosts: localhost gather_facts: false tasks: - name: "[resolve] Infisical creds from env" ansible.builtin.set_fact: infisical_client_id: "{{ lookup('env','INFISICAL_CLIENT_ID') }}" infisical_client_secret: "{{ lookup('env','INFISICAL_CLIENT_SECRET') }}" infisical_project_id: "{{ lookup('env','INFISICAL_PROJECT_ID') }}" infisical_url: "{{ lookup('env','INFISICAL_API_URL') | default('http://192.168.122.41:8080', true) }}" infisical_env_slug: "{{ lookup('env','INFISICAL_ENV_SLUG') | default('prod', true) }}" - name: "[resolve] Assert Infisical creds present" ansible.builtin.assert: that: - infisical_client_id | length > 0 - infisical_client_secret | length > 0 - infisical_project_id | length > 0 fail_msg: "Infisical creds missing from Semaphore environment 'infisical-dev'." - name: "[infisical] Log in" infisical.vault.login: url: "{{ infisical_url }}" auth_method: universal_auth universal_auth_client_id: "{{ infisical_client_id }}" universal_auth_client_secret: "{{ infisical_client_secret }}" register: infisical_login no_log: true - name: "[infisical] Read /lc-hosts" infisical.vault.read_secrets: login_data: "{{ infisical_login.login_data }}" project_id: "{{ infisical_project_id }}" env_slug: "{{ infisical_env_slug }}" path: "/lc-hosts" as_dict: true register: lc_secrets no_log: true - name: "[set] Stash thinkcenter sudo pass + login user" ansible.builtin.set_fact: thinkcenter_become_pass: "{{ lc_secrets.secrets['THINKCENTER_SUDO_PASS'] }}" thinkcenter_login_user: "{{ lc_secrets.secrets['THINKCENTER_USER'] }}" no_log: true # --- Play 2: REAPER + SWS + ReaPack on thinkcenter ONLY ---------------------- - name: "Install REAPER + SWS + ReaPack on thinkcenter" hosts: thinkcenter gather_facts: true become: true vars: ansible_become_pass: "{{ hostvars['localhost'].thinkcenter_become_pass }}" reaper_user: "{{ hostvars['localhost'].thinkcenter_login_user }}" reaper_version: "779" # 7.79 (dot removed) reaper_url: "https://www.reaper.fm/files/7.x/reaper779_linux_x86_64.tar.xz" reaper_install_dir: /opt reaper_binary: /opt/REAPER/reaper sws_url: "https://www.sws-extension.org/download/pre-release/sws-2.14.0.4-Linux-x86_64-e04d6ff1.tar.xz" sws_so_name: reaper_sws-x86_64.so reapack_url: "https://github.com/cfillion/reapack/releases/download/v1.2.6/reaper_reapack-x86_64.so" reapack_sha256: "35d80f63d8174c964af589c7d87c4728aa18f06899dce873e33f8d552d1bc7e0" tmp_dir: /tmp/reaper-install tasks: - name: "[safety] Confirm we are on thinkcenter" ansible.builtin.assert: that: inventory_hostname == 'thinkcenter' fail_msg: "Refusing to run — this play is scoped to thinkcenter only." - name: "[safety] Assert reaper_user resolved + exists" block: - name: "[safety] Assert non-empty" ansible.builtin.assert: that: reaper_user | length > 0 fail_msg: "reaper_user empty — THINKCENTER_USER not read from Infisical." - name: "[facts] getent the user" ansible.builtin.getent: database: passwd key: "{{ reaper_user }}" - name: "[facts] Assert user exists" ansible.builtin.assert: that: reaper_user in ansible_facts.getent_passwd fail_msg: "User '{{ reaper_user }}' does not exist on host." - name: "[deps] Ensure runtime deps + PipeWire-JACK bridge (retry)" ansible.builtin.apt: name: - xz-utils - libstdc++6 - libgl1 - libasound2t64 - pipewire-jack state: present update_cache: true cache_valid_time: 3600 register: deps until: deps is succeeded retries: 3 delay: 15 - name: "[audio] Add {{ reaper_user }} to the audio group (RT scheduling)" ansible.builtin.user: name: "{{ reaper_user }}" groups: audio append: true # ---------- REAPER core ---------- - name: "[reaper] Is REAPER already installed at {{ reaper_binary }}?" ansible.builtin.stat: path: "{{ reaper_binary }}" register: reaper_present - name: "[reaper] Check installed version (if present)" ansible.builtin.command: "{{ reaper_binary }} -version" register: reaper_installed_ver changed_when: false failed_when: false when: reaper_present.stat.exists - name: "[reaper] Create temp dir" ansible.builtin.file: path: "{{ tmp_dir }}" state: directory mode: "0755" when: not reaper_present.stat.exists - name: "[reaper] Download REAPER {{ reaper_version }} tarball (retry)" ansible.builtin.get_url: url: "{{ reaper_url }}" dest: "{{ tmp_dir }}/reaper.tar.xz" mode: "0644" register: reaper_dl until: reaper_dl is succeeded retries: 3 delay: 15 when: not reaper_present.stat.exists - name: "[reaper] Extract tarball" ansible.builtin.unarchive: src: "{{ tmp_dir }}/reaper.tar.xz" dest: "{{ tmp_dir }}" remote_src: true when: not reaper_present.stat.exists - name: "[reaper] Run installer unattended (--quiet, desktop integration)" ansible.builtin.command: cmd: "./install-reaper.sh --install {{ reaper_install_dir }} --integrate-desktop --usr-local-bin-symlink --quiet" chdir: "{{ tmp_dir }}/reaper_linux_x86_64" register: reaper_installer when: not reaper_present.stat.exists changed_when: reaper_installer.rc == 0 - name: "[reaper] Verify binary now exists" ansible.builtin.stat: path: "{{ reaper_binary }}" register: reaper_after - name: "[reaper] Assert REAPER installed" ansible.builtin.assert: that: reaper_after.stat.exists fail_msg: "REAPER binary not found at {{ reaper_binary }} after install." success_msg: "REAPER present at {{ reaper_binary }}." # ---------- User plugin dir ---------- - name: "[plugins] Resolve user home" ansible.builtin.set_fact: reaper_home: "{{ ansible_facts.getent_passwd[reaper_user][4] }}" - name: "[plugins] Ensure UserPlugins dir exists (owned by {{ reaper_user }})" ansible.builtin.file: path: "{{ reaper_home }}/.config/REAPER/UserPlugins" state: directory owner: "{{ reaper_user }}" group: "{{ reaper_user }}" mode: "0755" # ---------- SWS extension ---------- - name: "[sws] Download SWS tarball (retry)" ansible.builtin.get_url: url: "{{ sws_url }}" dest: "{{ tmp_dir }}/sws.tar.xz" mode: "0644" register: sws_dl until: sws_dl is succeeded retries: 3 delay: 15 - name: "[sws] Create SWS extract dir" ansible.builtin.file: path: "{{ tmp_dir }}/sws" state: directory mode: "0755" - name: "[sws] Extract SWS" ansible.builtin.unarchive: src: "{{ tmp_dir }}/sws.tar.xz" dest: "{{ tmp_dir }}/sws" remote_src: true - name: "[sws] Locate the SWS .so inside the archive" ansible.builtin.find: paths: "{{ tmp_dir }}/sws" patterns: "reaper_sws*.so" recurse: true register: sws_found - name: "[sws] Assert SWS .so found" ansible.builtin.assert: that: sws_found.files | length > 0 fail_msg: "Could not find reaper_sws*.so in the SWS archive." - name: "[sws] Install SWS .so into UserPlugins" ansible.builtin.copy: src: "{{ sws_found.files[0].path }}" dest: "{{ reaper_home }}/.config/REAPER/UserPlugins/{{ sws_so_name }}" owner: "{{ reaper_user }}" group: "{{ reaper_user }}" mode: "0644" remote_src: true register: sws_install # ---------- ReaPack ---------- - name: "[reapack] Download ReaPack .so with checksum verification (retry)" ansible.builtin.get_url: url: "{{ reapack_url }}" dest: "{{ reaper_home }}/.config/REAPER/UserPlugins/reaper_reapack-x86_64.so" checksum: "sha256:{{ reapack_sha256 }}" owner: "{{ reaper_user }}" group: "{{ reaper_user }}" mode: "0644" register: reapack_install until: reapack_install is succeeded retries: 3 delay: 15 # ---------- Verify + cleanup ---------- - name: "[verify] List installed REAPER user plugins" ansible.builtin.find: paths: "{{ reaper_home }}/.config/REAPER/UserPlugins" patterns: "*.so" register: installed_plugins - name: "[verify] Assert both extensions present" ansible.builtin.assert: that: - installed_plugins.files | map(attribute='path') | select('search','reaper_sws') | list | length > 0 - installed_plugins.files | map(attribute='path') | select('search','reaper_reapack') | list | length > 0 fail_msg: "SWS and/or ReaPack .so missing from UserPlugins." success_msg: "SWS + ReaPack installed into UserPlugins." - name: "[cleanup] Remove temp install dir" ansible.builtin.file: path: "{{ tmp_dir }}" state: absent - name: "[report] REAPER stack summary" ansible.builtin.debug: msg: - "===== thinkcenter REAPER stack =====" - "REAPER binary : {{ reaper_binary }} ({{ 'newly installed' if not reaper_present.stat.exists else 'already present' }})" - "UserPlugins dir : {{ reaper_home }}/.config/REAPER/UserPlugins" - "installed .so files : {{ installed_plugins.files | map(attribute='path') | map('basename') | list }}" - "audio group : {{ reaper_user }} added (log out/in for RT scheduling to take effect)" - "NOTE: launch REAPER, set Preferences->Audio->Device to JACK (PipeWire bridge) or ALSA. Extensions appear under the Extensions menu."