From 5818b05d96d9b84ed4d18cd4ce3ca016b6ecaf1d Mon Sep 17 00:00:00 2001 From: Alexandre Pires Date: Fri, 10 Jul 2026 17:15:24 -0400 Subject: [PATCH] Enhance Linux and macOS development station configurations: - Add user management and Oh My Zsh settings - Update service scripts to use Zsh - Include new packages and environment variables - Modify Terraform configuration for upstream services --- ansible/host_vars/dev-02.lab.alexpires.me.yml | 41 ++++++++++ ansible/host_vars/gpu-01.lab.alexpires.me.yml | 4 +- .../roles/linux_dev_station/defaults/main.yml | 26 +++++++ .../roles/linux_dev_station/tasks/main.yml | 74 +++++++++++++++++++ .../linux_dev_station/tasks/tool_packages.yml | 71 ++++++++++++++++++ .../templates/code-server.service.j2 | 3 +- .../templates/opencode.service.j2 | 3 +- .../templates/start-ide.sh.j2 | 11 ++- .../templates/start-opencode.sh.j2 | 11 ++- .../roles/macos_dev_station/defaults/main.yml | 19 +++++ .../roles/macos_dev_station/tasks/main.yml | 37 ++++++++++ terraform/apps/dev-01/config.tf | 4 +- 12 files changed, 297 insertions(+), 7 deletions(-) diff --git a/ansible/host_vars/dev-02.lab.alexpires.me.yml b/ansible/host_vars/dev-02.lab.alexpires.me.yml index b65794e..725fbc4 100644 --- a/ansible/host_vars/dev-02.lab.alexpires.me.yml +++ b/ansible/host_vars/dev-02.lab.alexpires.me.yml @@ -2,6 +2,23 @@ hostname: dev-02.lab.alexpires.me ansible_user: "{{ lookup('env', 'ANSIBLE_USER') }}" ansible_host: "10.19.4.210" +# Users +login_users: + - username: "{{ ansible_user }}" + comment: "Managed by Ansible" + sudoer: true + sudoer_root_only: false + sudoer_no_password: true + pubkey: "{{ lookup('file', 'keys/ansible_user.pub') }}" + password_disabled: true + - username: devstation + comment: "Managed by Ansible" + pubkey: "{{ lookup('file', 'keys/localnetwork.pub') }}" + shell: "/bin/zsh" + groups: + - "users" + password_disabled: true + linux_dev_station_service_user_enabled: true linux_dev_station_service_user: "devstation" linux_dev_station_service_home: "/home/devstation" @@ -165,3 +182,27 @@ fw_allowed_ports: - { rule: "allow", port: "3000", proto: "tcp", from: "10.19.4.0/24" } # code-server (local network only) - { rule: "allow", port: "4096", proto: "tcp", from: "10.19.4.0/24" } # opencode (local network only) - { rule: "allow", port: "9090", proto: "tcp", from: "10.19.4.0/24" } # opencode (local network only) + +linux_dev_station_oh_my_zsh_theme: "agnoster" +linux_dev_station_oh_my_zsh_plugins: + - git + - fzf + - common-aliases + - sudo + - tmux + - extract + - colored-man-pages + - history-substring-search + - kubectl + - ansible + - aws + - golang + - dnf + - bgnotify +linux_dev_station_zshrc_extra_env: + EDITOR: "nano" + MANPAGER: "less -R" + LESS: "-R" + BW_ORGANIZATION_ID: "{{ lookup('env', 'BW_ORGANIZATION_ID', default='') }}" + BW_ACCESS_TOKEN: "{{ lookup('env', 'BW_ACCESS_TOKEN', default='') }}" + ANSIBLE_USER: "{{ lookup('env', 'ANSIBLE_USER', default='') }}" diff --git a/ansible/host_vars/gpu-01.lab.alexpires.me.yml b/ansible/host_vars/gpu-01.lab.alexpires.me.yml index a6c59db..1ad4673 100644 --- a/ansible/host_vars/gpu-01.lab.alexpires.me.yml +++ b/ansible/host_vars/gpu-01.lab.alexpires.me.yml @@ -209,7 +209,7 @@ llama_server_argv_extra: "-b", 2048, "-ub", - 512, + 1024, "-fa", "on", "-ctk", @@ -236,6 +236,8 @@ llama_server_argv_extra: "--spec-draft-n-max", 2, "--swa-full", + "--cache-reuse", + 256 ] auto_suspend_enabled: true diff --git a/ansible/roles/linux_dev_station/defaults/main.yml b/ansible/roles/linux_dev_station/defaults/main.yml index 0e14a20..97895ab 100644 --- a/ansible/roles/linux_dev_station/defaults/main.yml +++ b/ansible/roles/linux_dev_station/defaults/main.yml @@ -25,6 +25,9 @@ linux_dev_station_dnf_packages: - golang - ripgrep - fzf + - sqlite3 + - opentofu + - kubectl linux_dev_station_tool_packages: - name: uv @@ -39,6 +42,9 @@ linux_dev_station_tool_packages: - name: opencode version: "1.17.18" sha256: "" + - name: sectool + version: "1.1.2" + sha256: "" linux_dev_station_code_server_host: "0.0.0.0" linux_dev_station_code_server_port: 3000 @@ -63,6 +69,26 @@ linux_dev_station_copilot_fail_on_error: false linux_dev_station_oh_my_zsh_enabled: true linux_dev_station_oh_my_zsh_install_url: "https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh" +linux_dev_station_oh_my_zsh_theme: "agnoster" +linux_dev_station_oh_my_zsh_plugins: + - git + - fzf + - common-aliases + - sudo + - tmux + - extract + - colored-man-pages + - history-substring-search + - kubectl + - microk8s + - docker + - docker-compose + - ansible + - aws + - golang + - dnf + - bgnotify +linux_dev_station_zshrc_extra_env: {} linux_dev_station_npm_packages: [] diff --git a/ansible/roles/linux_dev_station/tasks/main.yml b/ansible/roles/linux_dev_station/tasks/main.yml index 6ec8ad7..969f803 100644 --- a/ansible/roles/linux_dev_station/tasks/main.yml +++ b/ansible/roles/linux_dev_station/tasks/main.yml @@ -175,6 +175,80 @@ - linux_dev_station_service_user_enabled | bool - linux_dev_station_oh_my_zsh_enabled | bool +- name: Ensure .zshrc sets PATH for tool binaries + become: true + ansible.builtin.blockinfile: + path: "{{ linux_dev_station_service_home }}/.zshrc" + block: | + # ansible: linux_dev_station path + if ! [[ "$PATH" =~ "$HOME/.local/bin:" ]]; then + PATH="$HOME/.local/bin:$PATH" + fi + if ! [[ "$PATH" =~ "$HOME/bin:" ]]; then + PATH="$HOME/bin:$PATH" + fi + export PATH + marker: "# {mark} ansible: linux_dev_station path" + create: true + mode: "0644" + owner: "{{ linux_dev_station_service_user }}" + group: "{{ linux_dev_station_service_user }}" + when: + - linux_dev_station_service_user_enabled | bool + +- name: Ensure .zshrc unsets history vars before Oh My Zsh + become: true + ansible.builtin.blockinfile: + path: "{{ linux_dev_station_service_home }}/.zshrc" + block: | + # ansible: linux_dev_station unset history before oh-my-zsh + unset HISTFILE HISTSIZE SAVEHIST + marker: "# {mark} ansible: linux_dev_station unset history before oh-my-zsh" + create: true + mode: "0644" + owner: "{{ linux_dev_station_service_user }}" + group: "{{ linux_dev_station_service_user }}" + when: + - linux_dev_station_service_user_enabled | bool + - linux_dev_station_oh_my_zsh_enabled | bool + +- name: Ensure .zshrc sources Oh My Zsh + become: true + ansible.builtin.blockinfile: + path: "{{ linux_dev_station_service_home }}/.zshrc" + block: | + # ansible: linux_dev_station oh-my-zsh + export ZSH="$HOME/.oh-my-zsh" + ZSH_THEME="{{ linux_dev_station_oh_my_zsh_theme }}" + plugins=({{ linux_dev_station_oh_my_zsh_plugins | join(' ') }}) + source "$ZSH/oh-my-zsh.sh" + marker: "# {mark} ansible: linux_dev_station oh-my-zsh" + create: true + mode: "0644" + owner: "{{ linux_dev_station_service_user }}" + group: "{{ linux_dev_station_service_user }}" + when: + - linux_dev_station_service_user_enabled | bool + - linux_dev_station_oh_my_zsh_enabled | bool + +- name: Ensure .zshrc has extra environment variables + become: true + ansible.builtin.blockinfile: + path: "{{ linux_dev_station_service_home }}/.zshrc" + block: | + # ansible: linux_dev_station extra env vars + {% for env_key, env_value in linux_dev_station_zshrc_extra_env.items() %} + export {{ env_key }}="{{ env_value }}" + {% endfor %} + marker: "# {mark} ansible: linux_dev_station extra env vars" + create: true + mode: "0644" + owner: "{{ linux_dev_station_service_user }}" + group: "{{ linux_dev_station_service_user }}" + when: + - linux_dev_station_service_user_enabled | bool + - linux_dev_station_zshrc_extra_env | length > 0 + - name: Ensure .zshrc has ssh-agent startup for devstation become: true ansible.builtin.blockinfile: diff --git a/ansible/roles/linux_dev_station/tasks/tool_packages.yml b/ansible/roles/linux_dev_station/tasks/tool_packages.yml index 0459ec7..0e73e8f 100644 --- a/ansible/roles/linux_dev_station/tasks/tool_packages.yml +++ b/ansible/roles/linux_dev_station/tasks/tool_packages.yml @@ -142,6 +142,8 @@ become_user: "{{ linux_dev_station_service_user }}" ansible.builtin.command: >- {{ linux_dev_station_service_home }}/.fnm/fnm default {{ node_version }} + register: linux_dev_station_fnm_default_result + changed_when: "'default' in linux_dev_station_fnm_default_result.stderr or 'default' in linux_dev_station_fnm_default_result.stdout" environment: HOME: "{{ linux_dev_station_service_home }}" USER: "{{ linux_dev_station_service_user }}" @@ -266,3 +268,72 @@ when: - linux_dev_station_service_user_enabled | bool - linux_dev_station_tool_packages | selectattr('name', 'equalto', 'opencode') | list | length > 0 + +- name: Download sectool zip + become: true + ansible.builtin.get_url: + url: "https://github.com/a13labs/sectool/releases/download/v{{ tool.version }}/sectool-v{{ tool.version }}-linux-x64.zip" + dest: "{{ linux_dev_station_service_home }}/.cache/sectool-{{ tool.version }}.zip" + owner: "{{ linux_dev_station_service_user }}" + group: "{{ linux_dev_station_service_user }}" + mode: "0644" + vars: + tool: "{{ linux_dev_station_tool_packages | selectattr('name', 'equalto', 'sectool') | first }}" + when: + - linux_dev_station_service_user_enabled | bool + - linux_dev_station_tool_packages | selectattr('name', 'equalto', 'sectool') | list | length > 0 + +- name: Create sectool installation directories + become: true + become_user: "{{ linux_dev_station_service_user }}" + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: "0755" + loop: + - "{{ linux_dev_station_service_home }}/.local/lib/sectool-{{ tool.version }}" + - "{{ linux_dev_station_service_home }}/.local/bin" + vars: + tool: "{{ linux_dev_station_tool_packages | selectattr('name', 'equalto', 'sectool') | first }}" + when: + - linux_dev_station_service_user_enabled | bool + - linux_dev_station_tool_packages | selectattr('name', 'equalto', 'sectool') | list | length > 0 + +- name: Extract sectool zip to versioned directory + become: true + become_user: "{{ linux_dev_station_service_user }}" + ansible.builtin.unarchive: + src: "{{ linux_dev_station_service_home }}/.cache/sectool-{{ tool.version }}.zip" + dest: "{{ linux_dev_station_service_home }}/.local/lib/sectool-{{ tool.version }}" + remote_src: true + vars: + tool: "{{ linux_dev_station_tool_packages | selectattr('name', 'equalto', 'sectool') | first }}" + when: + - linux_dev_station_service_user_enabled | bool + - linux_dev_station_tool_packages | selectattr('name', 'equalto', 'sectool') | list | length > 0 + +- name: Ensure sectool binary is executable + become: true + ansible.builtin.file: + path: "{{ linux_dev_station_service_home }}/.local/lib/sectool-{{ tool.version }}/sectool" + mode: "0755" + owner: "{{ linux_dev_station_service_user }}" + group: "{{ linux_dev_station_service_user }}" + vars: + tool: "{{ linux_dev_station_tool_packages | selectattr('name', 'equalto', 'sectool') | first }}" + when: + - linux_dev_station_service_user_enabled | bool + - linux_dev_station_tool_packages | selectattr('name', 'equalto', 'sectool') | list | length > 0 + +- name: Create symlink for sectool binary + become: true + become_user: "{{ linux_dev_station_service_user }}" + ansible.builtin.file: + src: "{{ linux_dev_station_service_home }}/.local/lib/sectool-{{ tool.version }}/sectool" + dest: "{{ linux_dev_station_service_home }}/.local/bin/sectool" + state: link + vars: + tool: "{{ linux_dev_station_tool_packages | selectattr('name', 'equalto', 'sectool') | first }}" + when: + - linux_dev_station_service_user_enabled | bool + - linux_dev_station_tool_packages | selectattr('name', 'equalto', 'sectool') | list | length > 0 diff --git a/ansible/roles/linux_dev_station/templates/code-server.service.j2 b/ansible/roles/linux_dev_station/templates/code-server.service.j2 index 9396480..b534049 100644 --- a/ansible/roles/linux_dev_station/templates/code-server.service.j2 +++ b/ansible/roles/linux_dev_station/templates/code-server.service.j2 @@ -7,7 +7,7 @@ Type=simple User={{ linux_dev_station_service_user }} Group={{ linux_dev_station_service_user }} WorkingDirectory={{ linux_dev_station_workspace_dir }} -ExecStart=/bin/bash {{ linux_dev_station_scripts_dir }}/start-ide.sh +ExecStart=/bin/zsh -i {{ linux_dev_station_scripts_dir }}/start-ide.sh Restart=always RestartSec=5 StandardOutput=append:{{ linux_dev_station_logs_dir }}/code-server.log @@ -15,6 +15,7 @@ StandardError=append:{{ linux_dev_station_logs_dir }}/code-server.err.log Environment="HOME={{ linux_dev_station_service_home }}" Environment="PATH={{ linux_dev_station_path }}" +Environment="TMPDIR={{ linux_dev_station_service_home }}/.cache/tmp" Environment="PASSWORD={{ linux_dev_station_code_server_password }}" {% if linux_dev_station_ssh_agent_enabled | bool %} Environment="SSH_AUTH_SOCK={{ linux_dev_station_ssh_agent_socket }}" diff --git a/ansible/roles/linux_dev_station/templates/opencode.service.j2 b/ansible/roles/linux_dev_station/templates/opencode.service.j2 index 3a173b5..1d6750e 100644 --- a/ansible/roles/linux_dev_station/templates/opencode.service.j2 +++ b/ansible/roles/linux_dev_station/templates/opencode.service.j2 @@ -7,7 +7,7 @@ Type=simple User={{ linux_dev_station_service_user }} Group={{ linux_dev_station_service_user }} WorkingDirectory={{ linux_dev_station_workspace_dir }} -ExecStart=/bin/bash {{ linux_dev_station_scripts_dir }}/start-opencode.sh +ExecStart=/bin/zsh -i {{ linux_dev_station_scripts_dir }}/start-opencode.sh Restart=always RestartSec=5 StandardOutput=append:{{ linux_dev_station_logs_dir }}/opencode.log @@ -15,6 +15,7 @@ StandardError=append:{{ linux_dev_station_logs_dir }}/opencode.err.log Environment="HOME={{ linux_dev_station_service_home }}" Environment="PATH={{ linux_dev_station_path }}" +Environment="TMPDIR={{ linux_dev_station_service_home }}/.cache/tmp" Environment="OPENCODE_SERVER_USERNAME={{ linux_dev_station_opencode_server_username }}" Environment="OPENCODE_SERVER_PASSWORD={{ linux_dev_station_opencode_server_password }}" {% for env_key, env_value in linux_dev_station_opencode_extra_environment.items() %} diff --git a/ansible/roles/linux_dev_station/templates/start-ide.sh.j2 b/ansible/roles/linux_dev_station/templates/start-ide.sh.j2 index 3a18a00..4d5f602 100644 --- a/ansible/roles/linux_dev_station/templates/start-ide.sh.j2 +++ b/ansible/roles/linux_dev_station/templates/start-ide.sh.j2 @@ -1,7 +1,16 @@ -#!/usr/bin/env bash +#!/usr/bin/env zsh set -euo pipefail +# Suppress ZLE warnings from Oh My Zsh (no TTY in systemd) +setopt NOZLE 2>/dev/null || true + +# Override TMPDIR — /tmp is noexec on Fedora, Bun needs an executable temp +export TMPDIR="{{ linux_dev_station_service_home }}/.cache/tmp" +mkdir -p "$TMPDIR" +# Clean stale files older than 7 days +find "$TMPDIR" -mindepth 1 -atime +7 -delete 2>/dev/null || true + {% if linux_dev_station_ssh_agent_enabled | bool %} source "{{ linux_dev_station_scripts_dir }}/ssh-agent-start.sh" {% endif %} diff --git a/ansible/roles/linux_dev_station/templates/start-opencode.sh.j2 b/ansible/roles/linux_dev_station/templates/start-opencode.sh.j2 index 054a3c2..a307fcf 100644 --- a/ansible/roles/linux_dev_station/templates/start-opencode.sh.j2 +++ b/ansible/roles/linux_dev_station/templates/start-opencode.sh.j2 @@ -1,7 +1,16 @@ -#!/usr/bin/env bash +#!/usr/bin/env zsh set -euo pipefail +# Suppress ZLE warnings from Oh My Zsh (no TTY in systemd) +setopt NOZLE 2>/dev/null || true + +# Override TMPDIR — /tmp is noexec on Fedora, Bun needs an executable temp +export TMPDIR="{{ linux_dev_station_service_home }}/.cache/tmp" +mkdir -p "$TMPDIR" +# Clean stale files older than 7 days +find "$TMPDIR" -mindepth 1 -atime +7 -delete 2>/dev/null || true + {% if linux_dev_station_ssh_agent_enabled | bool %} source "{{ linux_dev_station_scripts_dir }}/ssh-agent-start.sh" {% endif %} diff --git a/ansible/roles/macos_dev_station/defaults/main.yml b/ansible/roles/macos_dev_station/defaults/main.yml index 16e53ab..14b1ff9 100644 --- a/ansible/roles/macos_dev_station/defaults/main.yml +++ b/ansible/roles/macos_dev_station/defaults/main.yml @@ -39,6 +39,25 @@ macos_dev_station_copilot_fail_on_error: false 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_oh_my_zsh_theme: "agnoster" +macos_dev_station_oh_my_zsh_plugins: + - git + - fzf + - common-aliases + - sudo + - tmux + - extract + - colored-man-pages + - history-substring-search + - kubectl + - docker + - docker-compose + - ansible + - aws + - golang + - brew + - bgnotify +macos_dev_station_zshrc_extra_env: {} macos_dev_station_colima_enabled: true macos_dev_station_colima_cpu: 4 diff --git a/ansible/roles/macos_dev_station/tasks/main.yml b/ansible/roles/macos_dev_station/tasks/main.yml index 43e319d..00719e2 100644 --- a/ansible/roles/macos_dev_station/tasks/main.yml +++ b/ansible/roles/macos_dev_station/tasks/main.yml @@ -165,6 +165,43 @@ - macos_dev_station_service_user_enabled | bool - macos_dev_station_oh_my_zsh_enabled | bool +- name: Ensure .zshrc sources Oh My Zsh + become: true + ansible.builtin.blockinfile: + path: "{{ macos_dev_station_service_home }}/.zshrc" + block: | + # ansible: macos_dev_station oh-my-zsh + export ZSH="$HOME/.oh-my-zsh" + ZSH_THEME="{{ macos_dev_station_oh_my_zsh_theme }}" + plugins=({{ macos_dev_station_oh_my_zsh_plugins | join(' ') }}) + source "$ZSH/oh-my-zsh.sh" + marker: "# {mark} ansible: macos_dev_station oh-my-zsh" + create: true + mode: "0644" + owner: "{{ macos_dev_station_service_user }}" + group: "{{ macos_dev_station_service_group }}" + when: + - macos_dev_station_service_user_enabled | bool + - macos_dev_station_oh_my_zsh_enabled | bool + +- name: Ensure .zshrc has extra environment variables + become: true + ansible.builtin.blockinfile: + path: "{{ macos_dev_station_service_home }}/.zshrc" + block: | + # ansible: macos_dev_station extra env vars + {% for env_key, env_value in macos_dev_station_zshrc_extra_env.items() %} + export {{ env_key }}="{{ env_value }}" + {% endfor %} + marker: "# {mark} ansible: macos_dev_station extra env vars" + create: true + mode: "0644" + owner: "{{ macos_dev_station_service_user }}" + group: "{{ macos_dev_station_service_group }}" + when: + - macos_dev_station_service_user_enabled | bool + - macos_dev_station_zshrc_extra_env | length > 0 + - name: Ensure .zshrc has ssh-agent startup for devstation become: true ansible.builtin.blockinfile: diff --git a/terraform/apps/dev-01/config.tf b/terraform/apps/dev-01/config.tf index e299386..0fdf09e 100644 --- a/terraform/apps/dev-01/config.tf +++ b/terraform/apps/dev-01/config.tf @@ -161,7 +161,7 @@ locals { http_port = 4096 tls = true fqdn = ["opencode.lab.alexpires.me"] - upstream = "http://10.19.4.203:4096" + upstream = "http://10.19.4.210:4096" read_timeout = "3600s" locations = [ @@ -178,7 +178,7 @@ locals { http_port = 3000 tls = true fqdn = ["vscode.lab.alexpires.me"] - upstream = "http://10.19.4.203:3000" + upstream = "http://10.19.4.210:3000" locations = [ { path = "/"