Add macOS development station playbook and role

- Created a new Ansible playbook for configuring macOS development stations.
- Added role `macos_dev_station` with default variables, handlers, and tasks.
- Implemented tasks for installing Homebrew packages, managing user configurations, and setting up launch agents for code-server and opencode.
- Included templates for launchd plist files and startup scripts for code-server and opencode.
- Added validation for required environment variables and configurations.
- Updated inventory to include macOS hosts and added a setup script for initial prerequisites.
- Modified Terraform configuration to include DNS records for new services.
This commit is contained in:
2026-07-04 15:37:35 +02:00
parent dfeaea3283
commit 040f7a382f
16 changed files with 1177 additions and 18 deletions
@@ -0,0 +1,86 @@
---
macos_dev_station_launchd_mode: "daemon" # daemon-only role
macos_dev_station_service_user_enabled: true
macos_dev_station_service_user: "devstation"
macos_dev_station_service_group: "staff"
macos_dev_station_service_shell: "/usr/bin/false"
macos_dev_station_service_user_ssh_enabled: false
macos_dev_station_service_user_ssh_shell: "/bin/zsh"
macos_dev_station_service_user_ssh_authorized_keys: []
macos_dev_station_service_home: "/Users/{{ macos_dev_station_service_user }}"
macos_dev_station_workspace_dir: "{{ macos_dev_station_service_home }}/projects"
macos_dev_station_scripts_dir: "{{ macos_dev_station_service_home }}/scripts"
macos_dev_station_logs_dir: "{{ macos_dev_station_service_home }}/Library/Logs"
macos_dev_station_launchd_plist_dir: "/Library/LaunchDaemons"
macos_dev_station_path: >-
{{ macos_dev_station_service_home }}/.local/bin:{{ macos_dev_station_service_home }}/go/bin:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
macos_dev_station_code_server_host: "0.0.0.0"
macos_dev_station_code_server_port: 3000
macos_dev_station_code_server_auth: "password"
macos_dev_station_code_server_password: "{{ lookup('env', 'CODE_SERVER_PASSWORD', default='') }}"
macos_dev_station_code_server_workspace: "{{ macos_dev_station_workspace_dir }}"
macos_dev_station_code_server_extensions_enabled: true
macos_dev_station_code_server_extensions_fail_on_error: false
macos_dev_station_code_server_extensions:
- ms-python.python
- ms-python.vscode-pylance
- ms-azuretools.vscode-docker
- hashicorp.terraform
- redhat.ansible
- eamodio.gitlens
macos_dev_station_oh_my_zsh_enabled: true
macos_dev_station_oh_my_zsh_install_url: "https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh"
macos_dev_station_colima_enabled: true
macos_dev_station_colima_cpu: 4
macos_dev_station_colima_memory: 8
macos_dev_station_brew_packages:
- git
- tmux
- wget
- curl
- jq
- openssl
- uv
- node
- python
- go
- rust
- docker
- colima
- code-server
macos_dev_station_npm_packages: []
macos_dev_station_git_init_default_branch: "main"
macos_dev_station_git_pull_rebase: false
macos_dev_station_ssh_key_path: "{{ ansible_facts['env']['HOME'] }}/.ssh/id_ed25519"
macos_dev_station_ssh_key_comment: "macmini-dev"
macos_dev_station_code_server_launchd_enabled: true
macos_dev_station_opencode_launchd_enabled: true
macos_dev_station_opencode_server_host: "127.0.0.1"
macos_dev_station_opencode_server_port: 4096
macos_dev_station_opencode_server_username: "{{ lookup('env', 'OPENCODE_SERVER_USERNAME', default='') }}"
macos_dev_station_opencode_server_password: "{{ lookup('env', 'OPENCODE_SERVER_PASSWORD', default='') }}"
macos_dev_station_opencode_extra_environment: {}
macos_dev_station_opencode_command: "opencode serve --hostname {{ macos_dev_station_opencode_server_host }} --port {{ macos_dev_station_opencode_server_port }}"
macos_dev_station_opencode_config: {}
macos_dev_station_opencode_config_path: "{{ macos_dev_station_service_home }}/.config/opencode/opencode.json"
# MCP installer definitions
# Supported kinds:
# - npm: package + optional version
# - uv: package + optional version + binary
# - go: module + optional version + binary
macos_dev_station_mcp_installers_enabled: true
macos_dev_station_mcp_installers: []
# Legacy ad-hoc hook kept for backward compatibility.
macos_dev_station_mcp_bootstrap_commands: []
macos_dev_station_manage_launchd: true
@@ -0,0 +1,60 @@
---
- name: Unload code-server launch agent if already loaded
become: true
ansible.builtin.command: >-
launchctl bootout system {{ macos_dev_station_launchd_plist_dir }}/com.a13labs.code-server.plist
failed_when: false
changed_when: false
when:
- macos_dev_station_manage_launchd | bool
- macos_dev_station_code_server_launchd_enabled | bool
listen: Restart code-server launch agent
- name: Load code-server launch agent
become: true
ansible.builtin.command: >-
launchctl bootstrap system {{ macos_dev_station_launchd_plist_dir }}/com.a13labs.code-server.plist
changed_when: true
when:
- macos_dev_station_manage_launchd | bool
- macos_dev_station_code_server_launchd_enabled | bool
listen: Restart code-server launch agent
- name: Enable code-server launch agent
become: true
ansible.builtin.command: launchctl enable system/com.a13labs.code-server
changed_when: false
when:
- macos_dev_station_manage_launchd | bool
- macos_dev_station_code_server_launchd_enabled | bool
listen: Restart code-server launch agent
- name: Unload opencode launch agent if already loaded
become: true
ansible.builtin.command: >-
launchctl bootout system {{ macos_dev_station_launchd_plist_dir }}/com.a13labs.opencode.plist
failed_when: false
changed_when: false
when:
- macos_dev_station_manage_launchd | bool
- macos_dev_station_opencode_launchd_enabled | bool
listen: Restart opencode launch agent
- name: Load opencode launch agent
become: true
ansible.builtin.command: >-
launchctl bootstrap system {{ macos_dev_station_launchd_plist_dir }}/com.a13labs.opencode.plist
changed_when: true
when:
- macos_dev_station_manage_launchd | bool
- macos_dev_station_opencode_launchd_enabled | bool
listen: Restart opencode launch agent
- name: Enable opencode launch agent
become: true
ansible.builtin.command: launchctl enable system/com.a13labs.opencode
changed_when: false
when:
- macos_dev_station_manage_launchd | bool
- macos_dev_station_opencode_launchd_enabled | bool
listen: Restart opencode launch agent
@@ -0,0 +1,445 @@
---
- name: Ensure host is macOS
ansible.builtin.assert:
that:
- ansible_system == "Darwin"
fail_msg: "Role macos_dev_station supports macOS hosts only"
- name: Ensure launchd mode is valid
ansible.builtin.assert:
that:
- macos_dev_station_launchd_mode == "daemon"
fail_msg: "macos_dev_station currently supports daemon mode only. Set macos_dev_station_launchd_mode to 'daemon'."
- name: Check passwordless sudo availability for daemon mode
ansible.builtin.command: sudo -n true
register: __macos_dev_station_sudo_check
changed_when: false
failed_when: false
- name: Ensure sudo access is available for daemon mode
ansible.builtin.assert:
that:
- __macos_dev_station_sudo_check.rc == 0 or (ansible_become_password | default('') | length > 0)
fail_msg: "Daemon mode needs sudo. Configure passwordless sudo for ansible user or set ANSIBLE_BECOME_PASSWORD via sectool."
- name: Ensure OpenCode server credentials are provided
ansible.builtin.assert:
that:
- macos_dev_station_opencode_server_username | length > 0
- macos_dev_station_opencode_server_password | length > 0
fail_msg: "Set OPENCODE_SERVER_USERNAME and OPENCODE_SERVER_PASSWORD in environment (recommended via sectool) before running the playbook."
when: macos_dev_station_opencode_launchd_enabled | bool
- name: Ensure code-server password is provided
ansible.builtin.assert:
that:
- macos_dev_station_code_server_password | length > 0
fail_msg: "Set CODE_SERVER_PASSWORD in environment (recommended via sectool) before running the playbook."
when:
- macos_dev_station_code_server_launchd_enabled | bool
- macos_dev_station_code_server_auth == "password"
- name: Install Homebrew packages
become: false
community.general.homebrew:
name: "{{ macos_dev_station_brew_packages }}"
state: present
- name: Ensure dedicated service user exists
become: true
ansible.builtin.user:
name: "{{ macos_dev_station_service_user }}"
shell: "{{ (macos_dev_station_service_user_ssh_enabled | bool) | ternary(macos_dev_station_service_user_ssh_shell, macos_dev_station_service_shell) }}"
home: "{{ macos_dev_station_service_home }}"
create_home: true
state: present
when: macos_dev_station_service_user_enabled | bool
- name: Discover npm executable path
ansible.builtin.command: /bin/sh -lc "command -v npm"
register: __macos_npm_path
changed_when: false
failed_when: __macos_npm_path.rc != 0
environment:
PATH: "{{ macos_dev_station_path }}"
when: macos_dev_station_npm_packages | length > 0
- name: Install global npm packages
community.general.npm:
name: "{{ item.name }}"
version: "{{ item.version }}"
executable: "{{ __macos_npm_path.stdout | trim }}"
global: true
environment:
PATH: "{{ macos_dev_station_path }}"
loop: "{{ macos_dev_station_npm_packages }}"
when: macos_dev_station_npm_packages | length > 0
- name: Ensure required user directories exist
become: true
ansible.builtin.file:
path: "{{ item }}"
state: directory
mode: "0750"
owner: "{{ macos_dev_station_service_user }}"
group: "{{ macos_dev_station_service_group }}"
loop:
- "{{ macos_dev_station_service_home }}"
- "{{ macos_dev_station_workspace_dir }}"
- "{{ macos_dev_station_scripts_dir }}"
- "{{ macos_dev_station_logs_dir }}"
- name: Ensure service home permissions are restrictive
become: true
ansible.builtin.file:
path: "{{ macos_dev_station_service_home }}"
state: directory
owner: "{{ macos_dev_station_service_user }}"
group: "{{ macos_dev_station_service_group }}"
mode: "0700"
- name: Ensure service user config directories exist
become: true
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "{{ macos_dev_station_service_user }}"
group: "{{ macos_dev_station_service_group }}"
mode: "0750"
loop:
- "{{ macos_dev_station_service_home }}/.cache"
- "{{ macos_dev_station_service_home }}/.local"
- "{{ macos_dev_station_service_home }}/.local/bin"
- "{{ macos_dev_station_service_home }}/.local/lib"
- "{{ macos_dev_station_service_home }}/.local/lib/node_modules"
- "{{ macos_dev_station_service_home }}/.config"
- "{{ macos_dev_station_service_home }}/.config/opencode"
when: macos_dev_station_service_user_enabled | bool
- name: Ensure OpenCode config object is valid when provided
ansible.builtin.assert:
that:
- macos_dev_station_opencode_config is mapping
fail_msg: "macos_dev_station_opencode_config must be a mapping (dictionary)."
- name: Render OpenCode config from host vars
become: true
ansible.builtin.template:
src: opencode.json.j2
dest: "{{ macos_dev_station_opencode_config_path }}"
owner: "{{ macos_dev_station_service_user }}"
group: "{{ macos_dev_station_service_group }}"
mode: "0600"
notify: Restart opencode launch agent
when:
- macos_dev_station_service_user_enabled | bool
- macos_dev_station_opencode_config | length > 0
- name: Download Oh My Zsh installer
become: true
ansible.builtin.get_url:
url: "{{ macos_dev_station_oh_my_zsh_install_url }}"
dest: "{{ macos_dev_station_service_home }}/.cache/ohmyzsh-install.sh"
owner: "{{ macos_dev_station_service_user }}"
group: "{{ macos_dev_station_service_group }}"
mode: "0755"
when:
- macos_dev_station_service_user_enabled | bool
- macos_dev_station_oh_my_zsh_enabled | bool
- name: Install Oh My Zsh for service user
become: true
become_user: "{{ macos_dev_station_service_user }}"
ansible.builtin.command: >-
/bin/sh {{ macos_dev_station_service_home }}/.cache/ohmyzsh-install.sh --unattended
args:
creates: "{{ macos_dev_station_service_home }}/.oh-my-zsh"
environment:
HOME: "{{ macos_dev_station_service_home }}"
USER: "{{ macos_dev_station_service_user }}"
RUNZSH: "no"
CHSH: "no"
KEEP_ZSHRC: "yes"
when:
- macos_dev_station_service_user_enabled | bool
- macos_dev_station_oh_my_zsh_enabled | bool
- name: Install managed MCP server runtimes
ansible.builtin.include_tasks: mcp_installers.yml
when:
- macos_dev_station_service_user_enabled | bool
- macos_dev_station_mcp_installers_enabled | bool
- macos_dev_station_mcp_installers | length > 0
- name: Run MCP bootstrap commands
become: true
become_user: "{{ macos_dev_station_service_user }}"
ansible.builtin.shell: "{{ item }}"
register: __mcp_bootstrap_result
changed_when: __mcp_bootstrap_result.rc == 0
args:
executable: /bin/sh
environment:
HOME: "{{ macos_dev_station_service_home }}"
USER: "{{ macos_dev_station_service_user }}"
PATH: "{{ macos_dev_station_path }}"
loop: "{{ macos_dev_station_mcp_bootstrap_commands }}"
when:
- macos_dev_station_service_user_enabled | bool
- macos_dev_station_mcp_bootstrap_commands | length > 0
- name: List installed code-server extensions for service user
become: true
become_user: "{{ macos_dev_station_service_user }}"
ansible.builtin.command: code-server --list-extensions
register: __code_server_extensions_installed
changed_when: false
environment:
HOME: "{{ macos_dev_station_service_home }}"
USER: "{{ macos_dev_station_service_user }}"
PATH: "{{ macos_dev_station_path }}"
when:
- macos_dev_station_service_user_enabled | bool
- macos_dev_station_code_server_extensions_enabled | bool
- macos_dev_station_code_server_extensions | length > 0
- name: Install base code-server extensions for service user
become: true
become_user: "{{ macos_dev_station_service_user }}"
ansible.builtin.command: >-
code-server --install-extension {{ item }}
register: __code_server_extension_install
changed_when: __code_server_extension_install.rc == 0
failed_when: >-
(macos_dev_station_code_server_extensions_fail_on_error | bool)
and (__code_server_extension_install.rc != 0)
environment:
HOME: "{{ macos_dev_station_service_home }}"
USER: "{{ macos_dev_station_service_user }}"
PATH: "{{ macos_dev_station_path }}"
notify: Restart code-server launch agent
loop: "{{ macos_dev_station_code_server_extensions }}"
when:
- macos_dev_station_service_user_enabled | bool
- macos_dev_station_code_server_extensions_enabled | bool
- item not in (__code_server_extensions_installed.stdout_lines | default([]))
- name: Report skipped code-server extensions that are unavailable
ansible.builtin.debug:
msg: >-
Skipping unavailable code-server extension '{{ item.item }}':
{{ (item.stderr_lines | default([])) | join(' ') }}
loop: "{{ __code_server_extension_install.results | default([]) }}"
when:
- macos_dev_station_service_user_enabled | bool
- macos_dev_station_code_server_extensions_enabled | bool
- not (macos_dev_station_code_server_extensions_fail_on_error | bool)
- item.rc is defined
- item.rc != 0
- name: Ensure devstation SSH directory exists when SSH access is enabled
become: true
ansible.builtin.file:
path: "{{ macos_dev_station_service_home }}/.ssh"
state: directory
owner: "{{ macos_dev_station_service_user }}"
group: "{{ macos_dev_station_service_group }}"
mode: "0700"
when:
- macos_dev_station_service_user_enabled | bool
- macos_dev_station_service_user_ssh_enabled | bool
- name: Ensure devstation authorized keys are present when SSH access is enabled
become: true
ansible.posix.authorized_key:
user: "{{ macos_dev_station_service_user }}"
key: "{{ item }}"
state: present
loop: "{{ macos_dev_station_service_user_ssh_authorized_keys }}"
when:
- macos_dev_station_service_user_enabled | bool
- macos_dev_station_service_user_ssh_enabled | bool
- name: Check whether macOS Remote Login allowlist group exists
become: true
ansible.builtin.command: dscl . -read /Groups/com.apple.access_ssh
register: __macos_access_ssh_group
changed_when: false
failed_when: false
when:
- macos_dev_station_service_user_enabled | bool
- macos_dev_station_service_user_ssh_enabled | bool
- name: Check whether service user is in macOS Remote Login allowlist
become: true
ansible.builtin.command: >-
dseditgroup -o checkmember -m {{ macos_dev_station_service_user }} com.apple.access_ssh
register: __macos_access_ssh_member
changed_when: false
failed_when: false
when:
- macos_dev_station_service_user_enabled | bool
- macos_dev_station_service_user_ssh_enabled | bool
- __macos_access_ssh_group.rc == 0
- name: Ensure service user is allowed by macOS Remote Login allowlist
become: true
ansible.builtin.command: >-
dseditgroup -o edit -a {{ macos_dev_station_service_user }} -t user com.apple.access_ssh
changed_when: true
when:
- macos_dev_station_service_user_enabled | bool
- macos_dev_station_service_user_ssh_enabled | bool
- __macos_access_ssh_group.rc == 0
- __macos_access_ssh_member.rc != 0
- name: Remove legacy user launch agent plists for hard mode
ansible.builtin.file:
path: "{{ ansible_facts['env']['HOME'] }}/Library/LaunchAgents/{{ item }}"
state: absent
loop:
- com.a13labs.code-server.plist
- com.a13labs.opencode.plist
- name: Ensure SSH directory exists
ansible.builtin.file:
path: "{{ ansible_facts['env']['HOME'] }}/.ssh"
state: directory
mode: "0700"
- name: Generate SSH key if missing
ansible.builtin.command: >-
ssh-keygen -t ed25519 -C {{ macos_dev_station_ssh_key_comment }}
-f {{ macos_dev_station_ssh_key_path }} -N ''
args:
creates: "{{ macos_dev_station_ssh_key_path }}"
- name: Configure Git init default branch
community.general.git_config:
name: init.defaultBranch
scope: global
value: "{{ macos_dev_station_git_init_default_branch }}"
- name: Configure Git pull.rebase
community.general.git_config:
name: pull.rebase
scope: global
value: "{{ macos_dev_station_git_pull_rebase | ternary('true', 'false') }}"
- name: Render code-server launcher script
become: true
ansible.builtin.template:
src: start-ide.sh.j2
dest: "{{ macos_dev_station_scripts_dir }}/start-ide.sh"
mode: "0755"
owner: "{{ macos_dev_station_service_user }}"
group: "{{ macos_dev_station_service_group }}"
- name: Render opencode launcher script
become: true
ansible.builtin.template:
src: start-opencode.sh.j2
dest: "{{ macos_dev_station_scripts_dir }}/start-opencode.sh"
mode: "0755"
owner: "{{ macos_dev_station_service_user }}"
group: "{{ macos_dev_station_service_group }}"
- name: Render tmux configuration
ansible.builtin.copy:
dest: "{{ ansible_facts['env']['HOME'] }}/.tmux.conf"
mode: "0644"
content: |
set -g mouse on
set -g history-limit 100000
- name: Ensure Colima runtime is started
ansible.builtin.command: >-
colima start --cpu {{ macos_dev_station_colima_cpu }} --memory {{ macos_dev_station_colima_memory }}
register: __colima_start
changed_when: "'already running' not in ((__colima_start.stdout | default('')) + (__colima_start.stderr | default('')))"
when: macos_dev_station_colima_enabled | bool
environment:
PATH: "{{ macos_dev_station_path }}"
- name: Render code-server launchd unit
become: true
ansible.builtin.template:
src: code-server.plist.j2
dest: "{{ macos_dev_station_launchd_plist_dir }}/com.a13labs.code-server.plist"
mode: "0644"
owner: root
group: wheel
when: macos_dev_station_code_server_launchd_enabled | bool
notify: Restart code-server launch agent
- name: Render opencode launchd unit
become: true
ansible.builtin.template:
src: opencode.plist.j2
dest: "{{ macos_dev_station_launchd_plist_dir }}/com.a13labs.opencode.plist"
mode: "0644"
owner: root
group: wheel
when: macos_dev_station_opencode_launchd_enabled | bool
notify: Restart opencode launch agent
- name: Check code-server daemon status
become: true
ansible.builtin.command: launchctl print system/com.a13labs.code-server
register: __code_server_daemon_status
failed_when: false
changed_when: false
when:
- macos_dev_station_manage_launchd | bool
- macos_dev_station_code_server_launchd_enabled | bool
- name: Bootstrap code-server daemon if missing
become: true
ansible.builtin.command: >-
launchctl bootstrap system {{ macos_dev_station_launchd_plist_dir }}/com.a13labs.code-server.plist
changed_when: true
when:
- macos_dev_station_manage_launchd | bool
- macos_dev_station_code_server_launchd_enabled | bool
- __code_server_daemon_status.rc != 0
- name: Ensure code-server daemon is enabled
become: true
ansible.builtin.command: launchctl enable system/com.a13labs.code-server
changed_when: false
when:
- macos_dev_station_manage_launchd | bool
- macos_dev_station_code_server_launchd_enabled | bool
- name: Check opencode daemon status
become: true
ansible.builtin.command: launchctl print system/com.a13labs.opencode
register: __opencode_daemon_status
failed_when: false
changed_when: false
when:
- macos_dev_station_manage_launchd | bool
- macos_dev_station_opencode_launchd_enabled | bool
- name: Bootstrap opencode daemon if missing
become: true
ansible.builtin.command: >-
launchctl bootstrap system {{ macos_dev_station_launchd_plist_dir }}/com.a13labs.opencode.plist
changed_when: true
when:
- macos_dev_station_manage_launchd | bool
- macos_dev_station_opencode_launchd_enabled | bool
- __opencode_daemon_status.rc != 0
- name: Ensure opencode daemon is enabled
become: true
ansible.builtin.command: launchctl enable system/com.a13labs.opencode
changed_when: false
when:
- macos_dev_station_manage_launchd | bool
- macos_dev_station_opencode_launchd_enabled | bool
- name: Ensure launch agents are loaded now
ansible.builtin.meta: flush_handlers
when: macos_dev_station_manage_launchd | bool
@@ -0,0 +1,132 @@
---
- name: Ensure MCP installer definitions list is valid
ansible.builtin.assert:
that:
- macos_dev_station_mcp_installers is iterable
fail_msg: "macos_dev_station_mcp_installers must be a list of installer definitions."
- name: Validate MCP installer kind values
ansible.builtin.assert:
that:
- item.kind is defined
- item.kind in ['npm', 'uv', 'go']
fail_msg: "MCP installer '{{ item.name | default('unnamed') }}' has unsupported kind '{{ item.kind | default('undefined') }}'. Supported: npm, uv, go."
loop: "{{ macos_dev_station_mcp_installers }}"
when: item.enabled | default(true) | bool
- name: Validate npm MCP installers
ansible.builtin.assert:
that:
- item.package is defined
- item.package | length > 0
fail_msg: "npm MCP installer '{{ item.name | default('unnamed') }}' requires 'package'."
loop: "{{ macos_dev_station_mcp_installers }}"
when:
- item.enabled | default(true) | bool
- item.kind == 'npm'
- name: Validate uv MCP installers
ansible.builtin.assert:
that:
- item.package is defined
- item.package | length > 0
- item.binary is defined
- item.binary | length > 0
fail_msg: "uv MCP installer '{{ item.name | default('unnamed') }}' requires 'package' and 'binary'."
loop: "{{ macos_dev_station_mcp_installers }}"
when:
- item.enabled | default(true) | bool
- item.kind == 'uv'
- name: Validate go MCP installers
ansible.builtin.assert:
that:
- item.module is defined
- item.module | length > 0
- item.binary is defined
- item.binary | length > 0
fail_msg: "go MCP installer '{{ item.name | default('unnamed') }}' requires 'module' and 'binary'."
loop: "{{ macos_dev_station_mcp_installers }}"
when:
- item.enabled | default(true) | bool
- item.kind == 'go'
- name: Discover npm executable path for MCP installers
ansible.builtin.command: /bin/sh -lc "command -v npm"
register: __macos_mcp_npm_path
changed_when: false
failed_when: __macos_mcp_npm_path.rc != 0
environment:
PATH: "{{ macos_dev_station_path }}"
when: (macos_dev_station_mcp_installers | selectattr('kind', 'equalto', 'npm') | list | length) > 0
- name: Install npm MCP packages for service user
become: true
become_user: "{{ macos_dev_station_service_user }}"
ansible.builtin.command:
argv:
- "{{ __macos_mcp_npm_path.stdout | trim }}"
- install
- --prefix
- "{{ macos_dev_station_service_home }}/.local"
- --global
- "{{ item.package }}@{{ item.version | default('latest') }}"
args:
creates: "{{ macos_dev_station_service_home }}/.local/lib/node_modules/{{ item.package }}"
register: __mcp_npm_install
changed_when: __mcp_npm_install.rc == 0
environment:
HOME: "{{ macos_dev_station_service_home }}"
USER: "{{ macos_dev_station_service_user }}"
PATH: "{{ macos_dev_station_path }}"
notify: Restart opencode launch agent
loop: "{{ macos_dev_station_mcp_installers }}"
when:
- item.enabled | default(true) | bool
- item.kind == 'npm'
- name: Install uv MCP tools for service user
become: true
become_user: "{{ macos_dev_station_service_user }}"
ansible.builtin.command:
argv:
- uv
- tool
- install
- "{{ item.package }}@{{ item.version | default('latest') }}"
args:
creates: "{{ macos_dev_station_service_home }}/.local/bin/{{ item.binary }}"
register: __mcp_uv_install
changed_when: __mcp_uv_install.rc == 0
environment:
HOME: "{{ macos_dev_station_service_home }}"
USER: "{{ macos_dev_station_service_user }}"
PATH: "{{ macos_dev_station_path }}"
notify: Restart opencode launch agent
loop: "{{ macos_dev_station_mcp_installers }}"
when:
- item.enabled | default(true) | bool
- item.kind == 'uv'
- name: Install go MCP binaries for service user
become: true
become_user: "{{ macos_dev_station_service_user }}"
ansible.builtin.command:
argv:
- go
- install
- "{{ item.module }}@{{ item.version | default('latest') }}"
args:
creates: "{{ macos_dev_station_service_home }}/.local/bin/{{ item.binary }}"
register: __mcp_go_install
changed_when: __mcp_go_install.rc == 0
environment:
HOME: "{{ macos_dev_station_service_home }}"
USER: "{{ macos_dev_station_service_user }}"
PATH: "{{ macos_dev_station_path }}"
GOBIN: "{{ macos_dev_station_service_home }}/.local/bin"
notify: Restart opencode launch agent
loop: "{{ macos_dev_station_mcp_installers }}"
when:
- item.enabled | default(true) | bool
- item.kind == 'go'
@@ -0,0 +1,40 @@
<?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>Label</key>
<string>com.a13labs.code-server</string>
<key>UserName</key>
<string>{{ macos_dev_station_service_user }}</string>
<key>WorkingDirectory</key>
<string>{{ macos_dev_station_workspace_dir }}</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>{{ macos_dev_station_scripts_dir }}/start-ide.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>{{ macos_dev_station_logs_dir }}/code-server.log</string>
<key>StandardErrorPath</key>
<string>{{ macos_dev_station_logs_dir }}/code-server.err.log</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>{{ macos_dev_station_path }}</string>
<key>PASSWORD</key>
<string>{{ macos_dev_station_code_server_password }}</string>
</dict>
</dict>
</plist>
@@ -0,0 +1 @@
{{ macos_dev_station_opencode_config | to_nice_json }}
@@ -0,0 +1,46 @@
<?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>Label</key>
<string>com.a13labs.opencode</string>
<key>UserName</key>
<string>{{ macos_dev_station_service_user }}</string>
<key>WorkingDirectory</key>
<string>{{ macos_dev_station_workspace_dir }}</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>{{ macos_dev_station_scripts_dir }}/start-opencode.sh</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>{{ macos_dev_station_logs_dir }}/opencode.log</string>
<key>StandardErrorPath</key>
<string>{{ macos_dev_station_logs_dir }}/opencode.err.log</string>
<key>EnvironmentVariables</key>
<dict>
<key>PATH</key>
<string>{{ macos_dev_station_path }}</string>
<key>OPENCODE_SERVER_USERNAME</key>
<string>{{ macos_dev_station_opencode_server_username }}</string>
<key>OPENCODE_SERVER_PASSWORD</key>
<string>{{ macos_dev_station_opencode_server_password }}</string>
{% for env_key, env_value in macos_dev_station_opencode_extra_environment.items() %}
<key>{{ env_key }}</key>
<string>{{ env_value }}</string>
{% endfor %}
</dict>
</dict>
</plist>
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -euo pipefail
cd {{ macos_dev_station_workspace_dir }}
exec code-server \
--bind-addr {{ macos_dev_station_code_server_host }}:{{ macos_dev_station_code_server_port }} \
--auth {{ macos_dev_station_code_server_auth }} \
--disable-telemetry \
{{ macos_dev_station_code_server_workspace }}
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
set -euo pipefail
cd {{ macos_dev_station_workspace_dir }}
exec {{ macos_dev_station_opencode_command }}