Skip to content

Instantly share code, notes, and snippets.

@reijovosu
Last active February 27, 2026 14:01
Show Gist options
  • Select an option

  • Save reijovosu/a14fb830829e20b2f427f9eb5ebb0b3d to your computer and use it in GitHub Desktop.

Select an option

Save reijovosu/a14fb830829e20b2f427f9eb5ebb0b3d to your computer and use it in GitHub Desktop.
---
# ansible-playbook -i localhost, -c local zsh-setup.yml -K
#
- name: Setup zsh + oh-my-zsh + plugins + hostname prompt (macOS + Linux)
hosts: all
gather_facts: true
vars:
# Configure for the SSH login user by default
target_user: "{{ ansible_user_id }}"
# Best-effort home defaults
target_home: >-
{{ (ansible_facts.os_family == 'Darwin')
| ternary('/Users/' ~ target_user, '/home/' ~ target_user) }}
zsh_path: /bin/zsh
zsh_plugins:
- {
name: "zsh-syntax-highlighting",
repo: "https://github.com/zsh-users/zsh-syntax-highlighting.git",
}
- {
name: "zsh-autosuggestions",
repo: "https://github.com/zsh-users/zsh-autosuggestions.git",
}
- {
name: "zsh-completions",
repo: "https://github.com/zsh-users/zsh-completions.git",
}
zsh_path_line: "export PATH=$HOME/bin:/usr/local/bin:$PATH"
zsh_plugins_line: "plugins=(git zsh-completions zsh-autosuggestions zsh-syntax-highlighting)"
# PROMPT modes:
# A) Always hostname:
zsh_prompt_line_hostname: "PROMPT='%{$fg_bold[green]%}➜%{$reset_color%} %{$fg_bold[blue]%}%m%{$reset_color%} %{$fg_bold[cyan]%}%~%{$reset_color%}${git_prompt_info:+ ${git_prompt_info}} %# '"
# B) Show localhost when local, hostname when SSH:
zsh_prompt_line_localhost: "PROMPT='%{$fg_bold[green]%}➜%{$reset_color%} %{$fg_bold[blue]%}${SSH_CONNECTION:+%m}${SSH_CONNECTION:-localhost}%{$reset_color%} %{$fg_bold[cyan]%}%~%{$reset_color%}${git_prompt_info:+ ${git_prompt_info}} %# '"
# Choose which prompt you want:
prompt_mode: "hostname" # or "localhost_when_local"
do_apt_upgrade: false
pre_tasks:
- name: macOS - ensure Homebrew exists
ansible.builtin.command: brew --version
register: brew_version
changed_when: false
failed_when: false
when: ansible_facts.os_family == "Darwin"
- name: macOS - fail if Homebrew is missing
ansible.builtin.fail:
msg: "Homebrew not found on this Mac. Install Homebrew first, then re-run this play."
when: ansible_facts.os_family == "Darwin" and brew_version.rc != 0
tasks:
# -------------------------
# Packages: Linux (Debian/Ubuntu family)
# -------------------------
- name: Linux - apt update
ansible.builtin.apt:
update_cache: true
cache_valid_time: 3600
become: true
when: ansible_facts.os_family == "Debian"
- name: Linux - apt upgrade (optional)
ansible.builtin.apt:
upgrade: dist
become: true
when: ansible_facts.os_family == "Debian" and do_apt_upgrade | bool
- name: Linux - install git and zsh
ansible.builtin.apt:
name: [git, zsh]
state: present
become: true
when: ansible_facts.os_family == "Debian"
# -------------------------
# Fix locale warnings on Debian/Ubuntu (perl / locale errors)
# -------------------------
- name: Linux - ensure locales package installed
ansible.builtin.apt:
name: locales
state: present
update_cache: true
become: true
when: ansible_facts.os_family == "Debian"
- name: Linux - ensure en_GB.UTF-8 locale is enabled in /etc/locale.gen
ansible.builtin.lineinfile:
path: /etc/locale.gen
regexp: '^#?\s*en_GB\.UTF-8\s+UTF-8\s*$'
line: 'en_GB.UTF-8 UTF-8'
become: true
when: ansible_facts.os_family == "Debian"
- name: Linux - generate locales
ansible.builtin.command: locale-gen
register: locale_gen
changed_when: "'Generating locales' in locale_gen.stdout or 'Generation complete' in locale_gen.stdout"
become: true
when: ansible_facts.os_family == "Debian"
- name: Linux - set default system locale
ansible.builtin.command: "update-locale LANG={{ linux_locale | default('en_GB.UTF-8') }} LC_ALL={{ linux_locale | default('en_GB.UTF-8') }}"
become: true
changed_when: false
when: ansible_facts.os_family == "Debian"
# -------------------------
# Packages: macOS (Homebrew)
# -------------------------
- name: macOS - install git and zsh with Homebrew
community.general.homebrew:
name: [git, zsh]
state: present
when: ansible_facts.os_family == "Darwin"
# -------------------------
# Set default shell to zsh
# -------------------------
- name: Linux - ensure zsh is the user's login shell
ansible.builtin.user:
name: "{{ target_user }}"
shell: "{{ zsh_path }}"
become: true
when: ansible_facts.os_family != "Darwin"
- name: macOS - attempt to set login shell (may require extra rights)
ansible.builtin.command: "chsh -s {{ zsh_path }} {{ target_user }}"
changed_when: false
failed_when: false
when: ansible_facts.os_family == "Darwin"
# -------------------------
# Oh My Zsh (idempotent via git)
# -------------------------
- name: Clone oh-my-zsh
ansible.builtin.git:
repo: "https://github.com/ohmyzsh/ohmyzsh.git"
dest: "{{ target_home }}/.oh-my-zsh"
version: master
update: true
- name: Ensure ~/.zshrc exists (copy template if missing)
ansible.builtin.copy:
src: "{{ target_home }}/.oh-my-zsh/templates/zshrc.zsh-template"
dest: "{{ target_home }}/.zshrc"
remote_src: true
owner: "{{ target_user }}"
mode: "0644"
force: false
# -------------------------
# Plugins
# -------------------------
- name: Ensure custom plugins directory exists
ansible.builtin.file:
path: "{{ target_home }}/.oh-my-zsh/custom/plugins"
state: directory
owner: "{{ target_user }}"
mode: "0755"
- name: Install zsh plugins
ansible.builtin.git:
repo: "{{ item.repo }}"
dest: "{{ target_home }}/.oh-my-zsh/custom/plugins/{{ item.name }}"
update: true
loop: "{{ zsh_plugins }}"
# -------------------------
# Update ~/.zshrc
# -------------------------
- name: Ensure PATH line exists in ~/.zshrc
ansible.builtin.lineinfile:
path: "{{ target_home }}/.zshrc"
regexp: '^export PATH=\$HOME/bin:/usr/local/bin:\$PATH$'
line: "{{ zsh_path_line }}"
create: true
owner: "{{ target_user }}"
mode: "0644"
- name: Ensure plugins line includes your plugins
ansible.builtin.lineinfile:
path: "{{ target_home }}/.zshrc"
regexp: '^plugins=\('
line: "{{ zsh_plugins_line }}"
owner: "{{ target_user }}"
- name: Ensure compinit line exists in ~/.zshrc
ansible.builtin.lineinfile:
path: "{{ target_home }}/.zshrc"
regexp: "^autoload -U compinit && compinit$"
line: "autoload -U compinit && compinit"
insertafter: EOF
owner: "{{ target_user }}"
- name: Choose prompt line based on prompt_mode
ansible.builtin.set_fact:
chosen_prompt_line: >-
{{ (prompt_mode == 'localhost_when_local')
| ternary(zsh_prompt_line_localhost, zsh_prompt_line_hostname) }}
- name: Ensure PROMPT line is set after oh-my-zsh loads
ansible.builtin.lineinfile:
path: "{{ target_home }}/.zshrc"
regexp: "^PROMPT="
line: "{{ chosen_prompt_line }}"
insertafter: '^source \$ZSH/oh-my-zsh\.sh'
owner: "{{ target_user }}"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment