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:
@@ -105,3 +105,48 @@ Create a python environment use:
|
||||
```
|
||||
python -m venv .venv --system-site-packages
|
||||
```
|
||||
|
||||
# macOS Dev Station
|
||||
|
||||
Use the repository hybrid flow: bootstrap once with shell script, then manage day-2 changes with Ansible.
|
||||
|
||||
## 1) Bootstrap prerequisites on the Mac
|
||||
|
||||
```
|
||||
./scripts/setup-dev-station.sh
|
||||
```
|
||||
|
||||
This installs only prerequisites (Homebrew, base tools, Ansible) and avoids persistent shell side effects.
|
||||
|
||||
## 2) Configure the Mac with Ansible
|
||||
|
||||
1. Add or adjust the host in `inventory/hosts` under `[darwin_dev]`.
|
||||
2. Configure host vars in `ansible/host_vars/mac-01.lab.alexpires.me.yml`.
|
||||
3. Run:
|
||||
|
||||
```
|
||||
cd ansible
|
||||
sectool exec ansible-playbook playbook_macos_dev_station.yml --limit darwin_dev
|
||||
```
|
||||
|
||||
The macOS role configures tools, tmux, code-server and opencode as system launchd daemons running under a dedicated service user. This allows services to start at boot without requiring an interactive login.
|
||||
|
||||
OpenCode server auth is injected from environment variables. Ensure `OPENCODE_SERVER_USERNAME` and `OPENCODE_SERVER_PASSWORD` are available through `ansible/sectool.env` values before running the playbook.
|
||||
code-server password auth is also env-backed. Ensure `CODE_SERVER_PASSWORD` is available through `ansible/sectool.env` values before running the playbook.
|
||||
Hard mode (system daemons + dedicated service user) also requires sudo rights. Use passwordless sudo for your ansible user or set `ANSIBLE_BECOME_PASSWORD` through `ansible/sectool.env` values before running the playbook.
|
||||
|
||||
## 3) Optional DNS entry in dev-01 CoreDNS
|
||||
|
||||
From `terraform/apps/dev-01`, you can inject an optional DNS record for the Mac dev station:
|
||||
|
||||
```
|
||||
export TF_VAR_mac_dev_station_dns_name="mac-mini"
|
||||
export TF_VAR_mac_dev_station_dns_type="A"
|
||||
export TF_VAR_mac_dev_station_dns_target="10.19.4.250"
|
||||
|
||||
sectool -f ../../../sectool.json exec tofu validate
|
||||
sectool -f ../../../sectool.json exec tofu plan
|
||||
sectool -f ../../../sectool.json exec tofu apply -auto-approve
|
||||
```
|
||||
|
||||
Use `CNAME` + hostname target instead of `A` when you want an alias.
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
hostname: mac-01.lab.alexpires.me
|
||||
ansible_user: "{{ lookup('env', 'ANSIBLE_USER') }}"
|
||||
|
||||
ansible_become_password: "{{ lookup('env', 'ANSIBLE_BECOME_PASSWORD', default='') }}"
|
||||
|
||||
macos_dev_station_launchd_mode: "daemon"
|
||||
macos_dev_station_service_user_enabled: true
|
||||
macos_dev_station_service_user: "devstation"
|
||||
macos_dev_station_service_group: "staff"
|
||||
macos_dev_station_service_home: "/Users/devstation"
|
||||
macos_dev_station_workspace_dir: "/Users/devstation/projects"
|
||||
macos_dev_station_scripts_dir: "/Users/devstation/scripts"
|
||||
macos_dev_station_logs_dir: "/Users/devstation/Library/Logs"
|
||||
|
||||
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_colima_enabled: true
|
||||
macos_dev_station_colima_cpu: 4
|
||||
macos_dev_station_colima_memory: 8
|
||||
|
||||
macos_dev_station_opencode_server_host: "0.0.0.0"
|
||||
macos_dev_station_opencode_server_port: 4096
|
||||
macos_dev_station_manage_launchd: true
|
||||
macos_dev_station_oh_my_zsh_enabled: true
|
||||
|
||||
macos_dev_station_code_server_extensions_enabled: true
|
||||
macos_dev_station_code_server_extensions:
|
||||
- ms-python.python
|
||||
- ms-python.vscode-pylance
|
||||
- ms-python.debugpy
|
||||
- golang.go
|
||||
- ms-azuretools.vscode-docker
|
||||
- hashicorp.terraform
|
||||
- opentofu.vscode-opentofu
|
||||
- redhat.ansible
|
||||
- redhat.vscode-yaml
|
||||
- eamodio.gitlens
|
||||
- sst-dev.opencode
|
||||
|
||||
macos_dev_station_mcp_bootstrap_commands: []
|
||||
|
||||
macos_dev_station_mcp_installers_enabled: true
|
||||
macos_dev_station_mcp_installers:
|
||||
- name: "chrome-devtools-mcp"
|
||||
kind: "npm"
|
||||
package: "chrome-devtools-mcp"
|
||||
version: "latest"
|
||||
- name: "playwright-mcp"
|
||||
kind: "npm"
|
||||
package: "@playwright/mcp"
|
||||
version: "latest"
|
||||
- name: "aws-documentation-mcp"
|
||||
kind: "uv"
|
||||
package: "awslabs.aws-documentation-mcp-server"
|
||||
version: "latest"
|
||||
binary: "awslabs.aws-documentation-mcp-server"
|
||||
- name: "gitea-mcp"
|
||||
kind: "go"
|
||||
module: "gitea.com/gitea/gitea-mcp"
|
||||
version: "latest"
|
||||
binary: "gitea-mcp"
|
||||
|
||||
macos_dev_station_opencode_extra_environment:
|
||||
GITEA_MCP_HOST: "https://code.alexpires.me"
|
||||
GITEA_APP_TOKEN: "{{ lookup('env', 'GITEA_APP_TOKEN', default='') }}"
|
||||
|
||||
macos_dev_station_opencode_config:
|
||||
"$schema": "https://opencode.ai/config.json"
|
||||
provider:
|
||||
alexpires-me:
|
||||
name: "alexpires.me"
|
||||
npm: "@ai-sdk/openai-compatible"
|
||||
options:
|
||||
baseURL: "https://open-webui.lab.alexpires.me/api"
|
||||
apiKey: "{{ lookup('env', 'OPEN_WEBUI_API_KEY', default='') }}"
|
||||
models:
|
||||
gemma-4-12b-q8-0:
|
||||
name: "gemma-4-12b-q8-0"
|
||||
gemma-4-26b-a4b-ud-iq4-xs:
|
||||
name: "gemma-4-26b-a4b-ud-iq4-xs"
|
||||
gemma-4-31b-iq4-xs:
|
||||
name: "gemma-4-31b-iq4-xs"
|
||||
gpt-oss-20b-q4-0:
|
||||
name: "gpt-oss-20b-q4-0"
|
||||
gpt-oss-20b-q8-0:
|
||||
name: "gpt-oss-20b-q8-0"
|
||||
qwen3-5-35b-a3b-q4-k-m:
|
||||
name: "qwen3-5-35b-a3b-q4-k-m"
|
||||
qwen3-5-9b-q8-0:
|
||||
name: "qwen3-5-9b-q8-0"
|
||||
qwen3-6-27b-mtp-q4-0:
|
||||
name: "qwen3-6-27b-mtp-q4-0"
|
||||
qwen3-6-27b-q4-k-m:
|
||||
name: "qwen3-6-27b-q4-k-m"
|
||||
qwen3-6-35b-a3b-mtp-ud-q4-m:
|
||||
name: "qwen3-6-35b-a3b-mtp-ud-q4-m"
|
||||
qwen3-6-35b-a3b-ud-q4-k-m:
|
||||
name: "qwen3-6-35b-a3b-ud-q4-k-m"
|
||||
"gemma4:e4b":
|
||||
name: "gemma4:e4b (scaleway)"
|
||||
"qwen3.5:9b":
|
||||
name: "qwen3.5:9b (scaleway)"
|
||||
gpt-oss-120b:
|
||||
name: "gpt-oss-120b (scaleway)"
|
||||
qwen3.5-397b-a17b:
|
||||
name: "qwen3.5-397b-a17b (scaleway)"
|
||||
qwen3.6-35b-a3b:
|
||||
name: "qwen3.6-35b-a3b (scaleway)"
|
||||
gemma-4-26b-a4b-it:
|
||||
name: "gemma-4-26b-a4b-it (scaleway)"
|
||||
mcp:
|
||||
deepwiki:
|
||||
type: "remote"
|
||||
url: "https://mcp.deepwiki.com/mcp"
|
||||
enabled: true
|
||||
terraform:
|
||||
type: "remote"
|
||||
url: "http://terraform-mcp.lab.alexpires.me/mcp"
|
||||
enabled: true
|
||||
opentofu:
|
||||
type: "remote"
|
||||
url: "https://mcp.opentofu.org/mcp"
|
||||
enabled: true
|
||||
microsoft_learn:
|
||||
type: "remote"
|
||||
url: "https://learn.microsoft.com/api/mcp"
|
||||
enabled: true
|
||||
mozilla_developer:
|
||||
type: "remote"
|
||||
url: "https://mcp.mdn.mozilla.net/"
|
||||
enabled: true
|
||||
chrome_devtools:
|
||||
type: "local"
|
||||
command:
|
||||
- "npx"
|
||||
- "-y"
|
||||
- "chrome-devtools-mcp@latest"
|
||||
- "--headless"
|
||||
- "--isolated"
|
||||
enabled: true
|
||||
playwright:
|
||||
type: "local"
|
||||
command:
|
||||
- "npx"
|
||||
- "-y"
|
||||
- "@playwright/mcp@latest"
|
||||
- "--headless"
|
||||
- "--isolated"
|
||||
enabled: true
|
||||
aws_documentation:
|
||||
type: "local"
|
||||
command:
|
||||
- "uvx"
|
||||
- "awslabs.aws-documentation-mcp-server@latest"
|
||||
enabled: true
|
||||
gitea:
|
||||
type: "local"
|
||||
command:
|
||||
- "gitea-mcp"
|
||||
- "-t"
|
||||
- "stdio"
|
||||
- "-H"
|
||||
- "{env:GITEA_MCP_HOST}"
|
||||
- "-T"
|
||||
- "{env:GITEA_APP_TOKEN}"
|
||||
enabled: true
|
||||
compaction:
|
||||
auto: true
|
||||
prune: true
|
||||
reserved: 32768
|
||||
|
||||
macos_dev_station_service_user_ssh_enabled: true
|
||||
macos_dev_station_service_user_ssh_authorized_keys:
|
||||
- >-
|
||||
ecdsa-sha2-nistp521
|
||||
AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAHHOPBR9p9kq5Cqzpe4cr3jHnweaYrHPXv5sXNzt+sCXP54uc5rWUBhxW2OQVvQzJ47dEVhEKi4WSC7LcuKS2G5AQDzWXNiasHvYIYQU3F/EknVCZnsiXYqXphYkJA6rJCNRnISZCIC1mocq6PB7J08ONdRFCvjfUBuVRT8BNGXNmQ/zQ==
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
- name: Configure macOS dev station
|
||||
hosts: darwin_dev
|
||||
gather_facts: true
|
||||
|
||||
pre_tasks:
|
||||
- name: Ensure target is macOS
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ansible_system == "Darwin"
|
||||
fail_msg: "This playbook supports macOS hosts only"
|
||||
|
||||
roles:
|
||||
- macos_dev_station
|
||||
@@ -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 }}
|
||||
+5
-1
@@ -16,4 +16,8 @@ CLOUDNS_AUTH_ID=$CLOUDNS_AUTH_ID
|
||||
CLOUDNS_PASSWORD=$CLOUDNS_PASSWORD
|
||||
WIN_PROVISION_PASSWORD=$WIN_PROVISION_PASSWORD
|
||||
WIN_GAME_PASSWORD=$WIN_GAME_PASSWORD
|
||||
WIN_CI_01_PASSWORD=$WIN_CI_01_PASSWORD
|
||||
WIN_CI_01_PASSWORD=$WIN_CI_01_PASSWORD
|
||||
OPENCODE_SERVER_USERNAME=$OPENCODE_SERVER_USERNAME
|
||||
OPENCODE_SERVER_PASSWORD=$OPENCODE_SERVER_PASSWORD
|
||||
CODE_SERVER_PASSWORD=$CODE_SERVER_PASSWORD
|
||||
OPEN_WEBUI_API_KEY=$OPEN_WEBUI_API_KEY
|
||||
|
||||
@@ -138,3 +138,6 @@ ci-01.lab.alexpires.me
|
||||
[windows_gitea_runner_windows]
|
||||
ci-01.lab.alexpires.me
|
||||
|
||||
[darwin_dev]
|
||||
mac-01.lab.alexpires.me
|
||||
|
||||
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_NAME="$(basename "$0")"
|
||||
LOG_FILE="${HOME}/.setup-dev-station.log"
|
||||
|
||||
log() {
|
||||
printf '[%s] %s\n' "${SCRIPT_NAME}" "$*" | tee -a "${LOG_FILE}"
|
||||
}
|
||||
|
||||
if [[ "$(uname -s)" != "Darwin" ]]; then
|
||||
log "This script is for macOS only."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Starting macOS bootstrap for dev station prerequisites."
|
||||
|
||||
if ! xcode-select -p >/dev/null 2>&1; then
|
||||
log "Installing Xcode Command Line Tools (follow GUI prompt once)."
|
||||
xcode-select --install || true
|
||||
else
|
||||
log "Xcode Command Line Tools already installed."
|
||||
fi
|
||||
|
||||
if ! command -v brew >/dev/null 2>&1; then
|
||||
log "Installing Homebrew."
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
else
|
||||
log "Homebrew already installed."
|
||||
fi
|
||||
|
||||
if [[ -x /opt/homebrew/bin/brew ]]; then
|
||||
eval "$(/opt/homebrew/bin/brew shellenv)"
|
||||
fi
|
||||
|
||||
if ! grep -Fq '/opt/homebrew/bin/brew shellenv' "${HOME}/.zprofile" 2>/dev/null; then
|
||||
log "Persisting Homebrew shellenv into ~/.zprofile."
|
||||
printf '\n# Homebrew\neval "$(/opt/homebrew/bin/brew shellenv)"\n' >> "${HOME}/.zprofile"
|
||||
else
|
||||
log "Homebrew shellenv already present in ~/.zprofile."
|
||||
fi
|
||||
|
||||
log "Updating Homebrew metadata."
|
||||
brew update
|
||||
|
||||
log "Installing bootstrap packages."
|
||||
brew install git curl jq python ansible
|
||||
|
||||
log "Preparing workspace directories."
|
||||
mkdir -p "${HOME}/projects" "${HOME}/scripts"
|
||||
|
||||
log "Bootstrap complete."
|
||||
log "Next step: run Ansible playbook for full dev-station configuration:"
|
||||
log " cd ~/projects/a13labs.infra/ansible"
|
||||
log " ansible-playbook playbook_macos_dev_station.yml --limit darwin_dev"
|
||||
@@ -32,7 +32,6 @@ locals {
|
||||
ip = "10.19.4.136"
|
||||
},
|
||||
]
|
||||
|
||||
zones = [
|
||||
{
|
||||
fqdn = "${local.lab_domain}"
|
||||
@@ -52,31 +51,21 @@ locals {
|
||||
type = "A"
|
||||
content = "10.19.4.136"
|
||||
},
|
||||
{
|
||||
name = "mac-01"
|
||||
type = "A"
|
||||
content = "10.19.4.203"
|
||||
},
|
||||
{
|
||||
name = "ollama"
|
||||
type = "CNAME"
|
||||
content = "dev-01.${local.lab_domain}."
|
||||
},
|
||||
{
|
||||
name = "kokoro"
|
||||
type = "CNAME"
|
||||
content = "gpu-01.${local.lab_domain}."
|
||||
},
|
||||
{
|
||||
name = "open-webui"
|
||||
type = "CNAME"
|
||||
content = "dev-01.${local.lab_domain}."
|
||||
},
|
||||
{
|
||||
name = "comfyui-backend"
|
||||
type = "CNAME"
|
||||
content = "gpu-01.${local.lab_domain}."
|
||||
},
|
||||
{
|
||||
name = "comfyui"
|
||||
type = "CNAME"
|
||||
content = "dev-01.${local.lab_domain}."
|
||||
},
|
||||
{
|
||||
name = "smb"
|
||||
type = "CNAME"
|
||||
@@ -131,7 +120,17 @@ locals {
|
||||
name = "terraform-mcp"
|
||||
type = "CNAME"
|
||||
content = "dev-01.${local.lab_domain}."
|
||||
}
|
||||
},
|
||||
{
|
||||
name = "opencode"
|
||||
type = "CNAME"
|
||||
content = "dev-01.${local.lab_domain}."
|
||||
},
|
||||
{
|
||||
name = "vscode"
|
||||
type = "CNAME"
|
||||
content = "dev-01.${local.lab_domain}."
|
||||
},
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -153,6 +152,38 @@ locals {
|
||||
# },
|
||||
# ]
|
||||
# }
|
||||
"opencode" = {
|
||||
http_port = 4096
|
||||
tls = true
|
||||
fqdn = ["opencode.lab.alexpires.me"]
|
||||
upstream = "http://10.19.4.203:4096"
|
||||
read_timeout = "3600s"
|
||||
|
||||
locations = [
|
||||
{
|
||||
path = "/"
|
||||
headers = {
|
||||
"Upgrade" = "$http_upgrade"
|
||||
"Connection" = "upgrade"
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
"code-server" = {
|
||||
http_port = 3000
|
||||
tls = true
|
||||
fqdn = ["vscode.lab.alexpires.me"]
|
||||
upstream = "http://10.19.4.203:3000"
|
||||
locations = [
|
||||
{
|
||||
path = "/"
|
||||
headers = {
|
||||
"Upgrade" = "$http_upgrade"
|
||||
"Connection" = "upgrade"
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
streams = {
|
||||
|
||||
Reference in New Issue
Block a user