diff --git a/.gitignore b/.gitignore index 795c74ded..8873aa016 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,5 @@ .DS_Store .ansible *.retry -roles* config.yml +galaxy_roles diff --git a/README.md b/README.md index 6fd53878a..105442812 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,8 @@ This playbook installs and configures most of the software I use on my Mac for w 3. Clone or download this repository to your local drive. 4. Run `ansible-galaxy install -r requirements.yml` inside this directory to install required Ansible roles. - 5. Run `ansible-playbook main.yml --ask-become-pass` inside this directory. Enter your macOS account password when prompted for the 'BECOME' password. + 5. Update the `~` for the actual full home directory path: `HOME_DIR=$(echo $HOME) && echo "Home directory: $HOME_DIR" && sed -i.bak "s|~|$HOME_DIR|g" default.config.yml` + 6. Run `ansible-playbook main.yml --ask-become-pass` inside this directory. Enter your macOS account password when prompted for the 'BECOME' password. > Note: If some Homebrew commands fail, you might need to agree to Xcode's license or fix some other Brew issue. Run `brew doctor` to see if this is the case. @@ -34,7 +35,7 @@ You can use this playbook to manage other Macs as well; the playbook doesn't eve Then edit the `inventory` file in this repository and change the line that starts with `127.0.0.1` to: -``` +```toml [ip address or hostname of mac] ansible_user=[mac ssh username] ``` diff --git a/ansible.cfg b/ansible.cfg index 532d6335d..890863f81 100644 --- a/ansible.cfg +++ b/ansible.cfg @@ -1,6 +1,7 @@ [defaults] nocows = True -roles_path = ./roles:/etc/ansible/roles +roles_path = $PWD/galaxy_roles:$PWD/roles inventory = inventory become = true stdout_callback = yaml +gather_facts = true diff --git a/default.config.yml b/default.config.yml index 35aca2970..3ab9a07b4 100644 --- a/default.config.yml +++ b/default.config.yml @@ -1,4 +1,5 @@ --- +configure_fonts: true configure_dotfiles: true configure_terminal: true configure_osx: true @@ -22,63 +23,77 @@ sudoers_custom_config: "" # # Allow users in admin group to use sudo with no password. # %admin ALL=(ALL) NOPASSWD: ALL -dotfiles_repo: https://github.com/geerlingguy/dotfiles.git +dotfiles_repo: https://github.com/armarquez/dotfiles.git dotfiles_repo_accept_hostkey: true -dotfiles_repo_local_destination: ~/Development/GitHub/dotfiles -dotfiles_repo_version: master # Replace with your default branch -dotfiles_files: - - .zshrc - - .gitignore - - .inputrc - - .osx - - .vimrc +dotfiles_repo_local_destination: "~/dotfiles" +dotfiles_repo_version: master +dotfiles_home: "~" homebrew_installed_packages: # - ansible # Installed via Pip. - autoconf - - bash-completion - - doxygen - - gettext - - gifsicle + - bazelisk + # - bash-completion + # - doxygen + - fzf + # - gettext + # - gifsicle - git - gh - go - gpg - httpie + - ical-buddy - iperf - libevent - sqlite - nmap - - node + # - node - nvm - - php - - pngpaste + # - php + # - pngpaste + - podman - ssh-copy-id - readline + - ripgrep + - rsync + - rustdesk - openssl - pv + - stow + - tldr - wget - - wrk + # - wrk + - zsh - zsh-history-substring-search homebrew_taps: [] homebrew_cask_appdir: /Applications homebrew_cask_apps: - - chromedriver - - docker + - 1password + - alfred + - bettertouchtool + # - chromedriver + # - docker - dropbox - firefox - google-chrome - - handbrake - - licecap - - sequel-ace + - google-drive + # - handbrake + - helpwire-operator + - iterm2 + # - licecap + # - sequel-ace - slack - - sublime-text + # - sublime-text + - tailscale-app - transmit + - visual-studio-code # See `geerlingguy.mac.mas` role documentation for usage instructions. -mas_installed_apps: [] +mas_installed_apps: + - { id: 937984704, name: "Amphetamine" } mas_email: "" mas_password: "" @@ -124,3 +139,11 @@ sublime_package_control: # Glob pattern to ansible task files to run after all other tasks are finished. post_provision_tasks: [] + +# iTerm2 configuration +iterm2_profiles_source_dir: "{{ playbook_dir }}/files/iterm2" +iterm2_dynamic_profiles_dest_dir: "~/Library/Application Support/iTerm2/DynamicProfiles" + +# OSX configuration +osx_script_source: "{{ playbook_dir }}/files/scripts/.osx" +osx_script_dest: "~/.osx" diff --git a/files/iterm2/boogey-iTerm.json b/files/iterm2/boogey-iTerm.json new file mode 100644 index 000000000..239431ae6 --- /dev/null +++ b/files/iterm2/boogey-iTerm.json @@ -0,0 +1,540 @@ +{ +"Profiles": [ +{ + "Ansi 7 Color (Light)" : { + "Green Component" : 0.73333334922790527, + "Red Component" : 0.73333334922790527, + "Blue Component" : 0.73333334922790527 + }, + "Ansi 15 Color (Light)" : { + "Green Component" : 1, + "Red Component" : 1, + "Blue Component" : 1 + }, + "Ansi 2 Color (Light)" : { + "Green Component" : 0.87031603506787336, + "Red Component" : 0.21895777543895642, + "Blue Component" : 0.13008742736566298 + }, + "Bold Color" : { + "Red Component" : 0.062745101749897003, + "Color Space" : "sRGB", + "Blue Component" : 0.062745101749897003, + "Alpha Component" : 1, + "Green Component" : 0.062745101749897003 + }, + "Ansi 1 Color (Dark)" : { + "Green Component" : 0, + "Red Component" : 1, + "Blue Component" : 0 + }, + "Use Bright Bold" : true, + "Ansi 9 Color (Light)" : { + "Green Component" : 0.052976857870817184, + "Red Component" : 0.95708823204040527, + "Blue Component" : 0.090362116694450378 + }, + "Ansi 8 Color (Dark)" : { + "Green Component" : 0.33333333333333331, + "Red Component" : 0.33333333333333331, + "Blue Component" : 0.33333333333333331 + }, + "Background Color" : { + "Red Component" : 0.97999999999999998, + "Color Space" : "sRGB", + "Blue Component" : 0.97999999999999998, + "Alpha Component" : 1, + "Green Component" : 0.97999999999999998 + }, + "Columns" : 80, + "Ansi 8 Color" : { + "Red Component" : 0.40781760215759277, + "Color Space" : "sRGB", + "Blue Component" : 0.4078223705291748, + "Alpha Component" : 1, + "Green Component" : 0.40782788395881653 + }, + "Right Option Key Sends" : 0, + "Ansi 4 Color (Light)" : { + "Green Component" : 0.37805050611495972, + "Red Component" : 0.079237513244152069, + "Blue Component" : 0.82438474893569946 + }, + "Has Hotkey" : false, + "Blinking Cursor" : false, + "Selected Text Color (Light)" : { + "Green Component" : 0.70916998386383057, + "Red Component" : 0.70916998386383057, + "Blue Component" : 0.70916998386383057 + }, + "Selected Text Color (Dark)" : { + "Green Component" : 0.70916998386383057, + "Red Component" : 0.70916998386383057, + "Blue Component" : 0.70916998386383057 + }, + "Ansi 3 Color (Dark)" : { + "Green Component" : 0.89706093072891235, + "Red Component" : 0.99814993143081665, + "Blue Component" : 0.039139740169048309 + }, + "Keyboard Map" : { + + }, + "Visual Bell" : true, + "Only The Default BG Color Uses Transparency" : true, + "Cursor Text Color" : { + "Red Component" : 1, + "Color Space" : "sRGB", + "Blue Component" : 1, + "Alpha Component" : 1, + "Green Component" : 1 + }, + "Scrollback Lines" : 1000, + "Selection Color (Light)" : { + "Green Component" : 0.20615114271640778, + "Red Component" : 0.09597768634557724, + "Blue Component" : 0.31055498123168945 + }, + "Ansi 0 Color" : { + "Red Component" : 0.078431375324726105, + "Color Space" : "sRGB", + "Blue Component" : 0.11764705926179886, + "Alpha Component" : 1, + "Green Component" : 0.098039217293262482 + }, + "Match Background Color (Dark)" : { + "Red Component" : 1, + "Color Space" : "P3", + "Blue Component" : 0, + "Alpha Component" : 1, + "Green Component" : 1 + }, + "Ansi 11 Color (Light)" : { + "Green Component" : 0.78536456823348999, + "Red Component" : 0.92833864688873291, + "Blue Component" : 0.035555899143218994 + }, + "Ansi 5 Color (Dark)" : { + "Green Component" : 0, + "Red Component" : 1, + "Blue Component" : 0.36536297202110291 + }, + "Cursor Text Color (Light)" : { + "Green Component" : 1, + "Red Component" : 0.99659550189971924, + "Blue Component" : 0.9480862021446228 + }, + "Silence Bell" : false, + "Rows" : 25, + "Draw Powerline Glyphs" : true, + "Guid" : "738EF4FA-2E7B-4324-AAD0-9B2168F7A350", + "Ansi 14 Color (Dark)" : { + "Green Component" : 0.89121149225051699, + "Red Component" : 0.41672572861338453, + "Blue Component" : 0.97867441177368164 + }, + "Ansi 15 Color (Dark)" : { + "Green Component" : 1, + "Red Component" : 1, + "Blue Component" : 1 + }, + "Ansi 0 Color (Dark)" : { + "Green Component" : 0, + "Red Component" : 0, + "Blue Component" : 0 + }, + "Ambiguous Double Width" : false, + "Option Key Sends" : 0, + "Ansi 3 Color" : { + "Red Component" : 0.78058648109436035, + "Color Space" : "sRGB", + "Blue Component" : 0, + "Alpha Component" : 1, + "Green Component" : 0.76959484815597534 + }, + "Window Type" : 0, + "BM Growl" : true, + "Prompt Before Closing 2" : false, + "Command" : "", + "Selected Text Color" : { + "Red Component" : 0, + "Color Space" : "sRGB", + "Blue Component" : 0, + "Alpha Component" : 1, + "Green Component" : 0 + }, + "Ansi 14 Color (Light)" : { + "Green Component" : 0.89121149225051699, + "Red Component" : 0.41672572861338453, + "Blue Component" : 0.97867441177368164 + }, + "Cursor Guide Color (Dark)" : { + "Red Component" : 0.74862593412399292, + "Color Space" : "P3", + "Blue Component" : 0.99125725030899048, + "Alpha Component" : 0.25, + "Green Component" : 0.92047786712646484 + }, + "Send Code When Idle" : false, + "Ansi 6 Color" : { + "Red Component" : 0, + "Color Space" : "sRGB", + "Blue Component" : 0.78166204690933228, + "Alpha Component" : 1, + "Green Component" : 0.77425903081893921 + }, + "Jobs to Ignore" : [ + "rlogin", + "ssh", + "slogin", + "telnet" + ], + "Badge Color (Dark)" : { + "Red Component" : 0.92929404973983765, + "Color Space" : "P3", + "Blue Component" : 0.13960540294647217, + "Alpha Component" : 0.5, + "Green Component" : 0.25479039549827576 + }, + "Cursor Color" : { + "Red Component" : 0, + "Color Space" : "sRGB", + "Blue Component" : 0, + "Alpha Component" : 1, + "Green Component" : 0 + }, + "Vertical Spacing" : 1, + "Bound Hosts" : [ + + ], + "Disable Window Resizing" : true, + "Close Sessions On End" : true, + "Selection Color (Dark)" : { + "Green Component" : 0.20615114271640778, + "Red Component" : 0.09597768634557724, + "Blue Component" : 0.31055498123168945 + }, + "Default Bookmark" : "No", + "Foreground Color (Light)" : { + "Green Component" : 1, + "Red Component" : 1, + "Blue Component" : 1 + }, + "Custom Command" : "No", + "Background Color (Dark)" : { + "Green Component" : 0.15233974158763885, + "Red Component" : 0.073702715337276459, + "Blue Component" : 0.21839480102062225 + }, + "Ansi 9 Color" : { + "Red Component" : 0.8659515380859375, + "Color Space" : "sRGB", + "Blue Component" : 0.45833224058151245, + "Alpha Component" : 1, + "Green Component" : 0.47524076700210571 + }, + "Ansi 14 Color" : { + "Red Component" : 0.37597531080245972, + "Color Space" : "sRGB", + "Blue Component" : 1, + "Alpha Component" : 1, + "Green Component" : 0.99263292551040649 + }, + "Flashing Bell" : false, + "Use Italic Font" : true, + "Ansi 13 Color (Dark)" : { + "Green Component" : 0.3333333432674408, + "Red Component" : 1, + "Blue Component" : 1 + }, + "Minimum Contrast" : 0, + "Cursor Guide Color (Light)" : { + "Red Component" : 0.74862593412399292, + "Color Space" : "P3", + "Blue Component" : 0.99125725030899048, + "Alpha Component" : 0.25, + "Green Component" : 0.92047786712646484 + }, + "Ansi 12 Color" : { + "Red Component" : 0.65349078178405762, + "Color Space" : "sRGB", + "Blue Component" : 0.9485321044921875, + "Alpha Component" : 1, + "Green Component" : 0.67044717073440552 + }, + "Ansi 10 Color (Light)" : { + "Green Component" : 0.81519794464111328, + "Red Component" : 0.23300750553607941, + "Blue Component" : 0.11517760157585144 + }, + "Non-ASCII Anti Aliased" : true, + "Ansi 10 Color" : { + "Red Component" : 0.3450070321559906, + "Color Space" : "sRGB", + "Blue Component" : 0.56541937589645386, + "Alpha Component" : 1, + "Green Component" : 0.9042816162109375 + }, + "Foreground Color" : { + "Red Component" : 0.062745101749897003, + "Color Space" : "sRGB", + "Blue Component" : 0.062745101749897003, + "Alpha Component" : 1, + "Green Component" : 0.062745101749897003 + }, + "Link Color (Light)" : { + "Red Component" : 0.14513972401618958, + "Color Space" : "P3", + "Blue Component" : 0.7093239426612854, + "Alpha Component" : 1, + "Green Component" : 0.35333043336868286 + }, + "Description" : "Default", + "Ansi 7 Color (Dark)" : { + "Green Component" : 0.73333334922790527, + "Red Component" : 0.73333334922790527, + "Blue Component" : 0.73333334922790527 + }, + "Sync Title" : false, + "Ansi 1 Color" : { + "Red Component" : 0.7074432373046875, + "Color Space" : "sRGB", + "Blue Component" : 0.16300037503242493, + "Alpha Component" : 1, + "Green Component" : 0.23660069704055786 + }, + "Name" : "Boogey", + "Transparency" : 0.05114140070921986, + "Horizontal Spacing" : 1, + "Cursor Color (Dark)" : { + "Green Component" : 0.79959547519683838, + "Red Component" : 0.94297069311141968, + "Blue Component" : 0.03614787757396698 + }, + "Ansi 2 Color (Dark)" : { + "Green Component" : 0.87031603506787336, + "Red Component" : 0.21895777543895642, + "Blue Component" : 0.13008742736566298 + }, + "Ansi 9 Color (Dark)" : { + "Green Component" : 0.052976857870817184, + "Red Component" : 0.95708823204040527, + "Blue Component" : 0.090362116694450378 + }, + "Badge Color" : { + "Red Component" : 0.74613857269287109, + "Color Space" : "sRGB", + "Blue Component" : 0.11610633134841919, + "Alpha Component" : 0.5, + "Green Component" : 0.11610633134841919 + }, + "Ansi 13 Color (Light)" : { + "Green Component" : 0.3333333432674408, + "Red Component" : 1, + "Blue Component" : 1 + }, + "Idle Code" : 0, + "Ansi 4 Color" : { + "Red Component" : 0.15404300391674042, + "Color Space" : "sRGB", + "Blue Component" : 0.78216177225112915, + "Alpha Component" : 1, + "Green Component" : 0.26474356651306152 + }, + "Bold Color (Dark)" : { + "Green Component" : 0.98771905899047852, + "Red Component" : 0.96919095516204834, + "Blue Component" : 1 + }, + "Screen" : -1, + "Ansi 4 Color (Dark)" : { + "Green Component" : 0.37805050611495972, + "Red Component" : 0.079237513244152069, + "Blue Component" : 0.82438474893569946 + }, + "Cursor Text Color (Dark)" : { + "Green Component" : 1, + "Red Component" : 0.99659550189971924, + "Blue Component" : 0.9480862021446228 + }, + "Selection Color" : { + "Red Component" : 0.70196080207824707, + "Color Space" : "sRGB", + "Blue Component" : 1, + "Alpha Component" : 1, + "Green Component" : 0.84313726425170898 + }, + "Use Non-ASCII Font" : false, + "Badge Color (Light)" : { + "Red Component" : 0.92929404973983765, + "Color Space" : "P3", + "Blue Component" : 0.13960540294647217, + "Alpha Component" : 0.5, + "Green Component" : 0.25479039549827576 + }, + "Character Encoding" : 4, + "Ansi 11 Color (Dark)" : { + "Green Component" : 0.78536456823348999, + "Red Component" : 0.92833864688873291, + "Blue Component" : 0.035555899143218994 + }, + "Bold Color (Light)" : { + "Green Component" : 0.98771905899047852, + "Red Component" : 0.96919095516204834, + "Blue Component" : 1 + }, + "Ansi 12 Color (Dark)" : { + "Green Component" : 0.3333333432674408, + "Red Component" : 0.3333333432674408, + "Blue Component" : 1 + }, + "Faint Text Alpha (Dark)" : 0.56850678066037741, + "Ansi 7 Color" : { + "Red Component" : 0.7810397744178772, + "Color Space" : "sRGB", + "Blue Component" : 0.78104829788208008, + "Alpha Component" : 1, + "Green Component" : 0.78105825185775757 + }, + "Ansi 6 Color (Dark)" : { + "Green Component" : 0.73333334922790527, + "Red Component" : 0, + "Blue Component" : 0.73333334922790527 + }, + "Non Ascii Font" : "Monaco 12", + "Cursor Guide Color" : { + "Red Component" : 0.52338260412216187, + "Color Space" : "sRGB", + "Blue Component" : 0.85319280624389648, + "Alpha Component" : 0.25, + "Green Component" : 0.77217715978622437 + }, + "Custom Directory" : "No", + "Working Directory" : "\/Users\/anthony_marquez", + "ASCII Anti Aliased" : true, + "Shortcut" : "", + "Mouse Reporting" : true, + "Tags" : [ + + ], + "Ansi 6 Color (Light)" : { + "Green Component" : 0.73333334922790527, + "Red Component" : 0, + "Blue Component" : 0.73333334922790527 + }, + "Match Background Color (Light)" : { + "Red Component" : 1, + "Color Space" : "P3", + "Blue Component" : 0, + "Alpha Component" : 1, + "Green Component" : 1 + }, + "Background Image Location" : "", + "Ansi 1 Color (Light)" : { + "Green Component" : 0, + "Red Component" : 1, + "Blue Component" : 0 + }, + "Use Bold Font" : true, + "Ansi 8 Color (Light)" : { + "Green Component" : 0.33333333333333331, + "Red Component" : 0.33333333333333331, + "Blue Component" : 0.33333333333333331 + }, + "Ansi 2 Color" : { + "Red Component" : 0, + "Color Space" : "sRGB", + "Blue Component" : 0, + "Alpha Component" : 1, + "Green Component" : 0.7607843279838562 + }, + "Normal Font" : "MesloLGS-NF-Regular 13", + "Unlimited Scrollback" : false, + "Ansi 12 Color (Light)" : { + "Green Component" : 0.3333333432674408, + "Red Component" : 0.3333333432674408, + "Blue Component" : 1 + }, + "Ansi 10 Color (Dark)" : { + "Green Component" : 0.81519794464111328, + "Red Component" : 0.23300750553607941, + "Blue Component" : 0.11517760157585144 + }, + "Ansi 3 Color (Light)" : { + "Green Component" : 0.89706093072891235, + "Red Component" : 0.99814993143081665, + "Blue Component" : 0.039139740169048309 + }, + "Cursor Color (Light)" : { + "Green Component" : 0.79959547519683838, + "Red Component" : 0.94297069311141968, + "Blue Component" : 0.03614787757396698 + }, + "Ansi 15 Color" : { + "Red Component" : 0.99999600648880005, + "Color Space" : "sRGB", + "Blue Component" : 1, + "Alpha Component" : 1, + "Green Component" : 1 + }, + "Blur" : false, + "Use Separate Colors for Light and Dark Mode" : true, + "Background Color (Light)" : { + "Green Component" : 0.15233974158763885, + "Red Component" : 0.073702715337276459, + "Blue Component" : 0.21839480102062225 + }, + "Terminal Type" : "xterm-256color", + "Ansi 13 Color" : { + "Red Component" : 0.8821563720703125, + "Color Space" : "sRGB", + "Blue Component" : 0.8821563720703125, + "Alpha Component" : 1, + "Green Component" : 0.4927266538143158 + }, + "Ansi 5 Color (Light)" : { + "Green Component" : 0, + "Red Component" : 1, + "Blue Component" : 0.36536297202110291 + }, + "Foreground Color (Dark)" : { + "Green Component" : 1, + "Red Component" : 1, + "Blue Component" : 1 + }, + "Link Color" : { + "Red Component" : 0.19802422821521759, + "Color Space" : "sRGB", + "Blue Component" : 0.9337158203125, + "Alpha Component" : 1, + "Green Component" : 0.55789834260940552 + }, + "Link Color (Dark)" : { + "Red Component" : 0.14513972401618958, + "Color Space" : "P3", + "Blue Component" : 0.7093239426612854, + "Alpha Component" : 1, + "Green Component" : 0.35333043336868286 + }, + "Ansi 11 Color" : { + "Red Component" : 0.9259033203125, + "Color Space" : "sRGB", + "Blue Component" : 0, + "Alpha Component" : 1, + "Green Component" : 0.8833775520324707 + }, + "Ansi 5 Color" : { + "Red Component" : 0.752197265625, + "Color Space" : "sRGB", + "Blue Component" : 0.74494361877441406, + "Alpha Component" : 1, + "Green Component" : 0.24931684136390686 + }, + "Ansi 0 Color (Light)" : { + "Green Component" : 0, + "Red Component" : 0, + "Blue Component" : 0 + } +} +] +} diff --git a/files/scripts/.osx b/files/scripts/.osx new file mode 100755 index 000000000..bdaaf50cb --- /dev/null +++ b/files/scripts/.osx @@ -0,0 +1,274 @@ +#!/usr/bin/env bash + +# Mac OS X configuration +# +# This configuration applies to the latest version of macOS (currently 11.3), +# and sets up preferences and configurations for all the built-in services and +# apps. Third-party app config should be done elsewhere. +# +# Options: +# --no-restart: Don't restart any apps or services after running the script. +# +# If you want to figure out what default needs changing, do the following: +# +# 1. `cd /tmp` +# 2. Store current defaults in file: `defaults read > before` +# 3. Make a change to your system. +# 4. Store new defaults in file: `defaults read > after` +# 5. Diff the files: `diff before after` +# +# @see: http://secrets.blacktree.com/?showapp=com.apple.finder +# @see: https://github.com/herrbischoff/awesome-macos-command-line +# +# @author Jeff Geerling + +# Warn that some commands will not be run if the script is not run as root. +if [[ $EUID -ne 0 ]]; then + RUN_AS_ROOT=false + printf "Certain commands will not be run without sudo privileges. To run as root, run the same command prepended with 'sudo', for example: $ sudo $0\n\n" | fold -s -w 80 +else + RUN_AS_ROOT=true + # Update existing `sudo` timestamp until `.osx` has finished + while true; do sudo -n true; sleep 60; kill -0 "$$" || exit; done 2>/dev/null & +fi + +############################################################################### +# General UI/UX # +############################################################################### + +# Expand save panel by default +defaults write NSGlobalDomain NSNavPanelExpandedStateForSaveMode -bool true + +# Expand print panel by default +defaults write NSGlobalDomain PMPrintingExpandedStateForPrint -bool true + +# Save to disk (not to iCloud) by default +defaults write NSGlobalDomain NSDocumentSaveNewDocumentsToCloud -bool false + +# Automatically quit printer app once the print jobs complete +defaults write com.apple.print.PrintingPrefs "Quit When Finished" -bool true + +# Restart automatically if the computer freezes +if [[ "$RUN_AS_ROOT" = true ]]; then + systemsetup -setrestartfreeze on +fi + +# Disable smart quotes as they’re annoying when typing code +defaults write NSGlobalDomain NSAutomaticQuoteSubstitutionEnabled -bool false + +# Disable smart dashes as they’re annoying when typing code +defaults write NSGlobalDomain NSAutomaticDashSubstitutionEnabled -bool false + +# Set background to dark-grey color +osascript -e 'tell application "Finder" to set desktop picture to POSIX file "/System/Library/Desktop Pictures/Solid Colors/Stone.png"' + +############################################################################### +# Trackpad, mouse, keyboard, Bluetooth accessories, and input # +############################################################################### + +# Trackpad: Haptic feedback (light, silent clicking) +defaults write com.apple.AppleMultitouchTrackpad FirstClickThreshold -int 0 +defaults write com.apple.AppleMultitouchTrackpad ActuationStrength -int 0 + +# Trackpad: map bottom right corner to right-click (requires restart!) +defaults write com.apple.driver.AppleBluetoothMultitouch.trackpad TrackpadRightClick -bool true +defaults write com.apple.AppleMultitouchTrackpad TrackpadRightClick -bool true +defaults write com.apple.AppleMultitouchTrackpad TrackpadCornerSecondaryClick -int 2 +defaults write NSGlobalDomain ContextMenuGesture -int 1 + +# Disable press-and-hold for keys in favor of key repeat +defaults write NSGlobalDomain ApplePressAndHoldEnabled -bool false + +# Set a blazingly fast keyboard repeat rate, and make it happen more quickly. +# (The KeyRepeat option requires logging out and back in to take effect.) +defaults write NSGlobalDomain InitialKeyRepeat -int 20 +defaults write NSGlobalDomain KeyRepeat -int 1 + +# Disable auto-correct +defaults write NSGlobalDomain NSAutomaticSpellingCorrectionEnabled -bool false + +############################################################################### +# Screen # +############################################################################### + +# Save screenshots to Downloads folder. +defaults write com.apple.screencapture location -string "${HOME}/Downloads" + +# Save screenshots in PNG format (other options: BMP, GIF, JPG, PDF, TIFF) +defaults write com.apple.screencapture type -string "png" + +# Disable shadow in screenshots +defaults write com.apple.screencapture disable-shadow -bool true + +############################################################################### +# Finder # +############################################################################### + +# Set Desktop as the default location for new Finder windows +# For other paths, use `PfLo` and `file:///full/path/here/` +defaults write com.apple.finder NewWindowTarget -string "PfDe" +defaults write com.apple.finder NewWindowTargetPath -string "file://${HOME}/Desktop/" + +# Show icons for hard drives, servers, and removable media on the desktop +defaults write com.apple.finder ShowExternalHardDrivesOnDesktop -bool true +defaults write com.apple.finder ShowHardDrivesOnDesktop -bool true +defaults write com.apple.finder ShowMountedServersOnDesktop -bool true +defaults write com.apple.finder ShowRemovableMediaOnDesktop -bool true + +# Finder: show hidden files by default +# defaults write com.apple.finder AppleShowAllFiles -bool true + +# Finder: show all filename extensions +defaults write NSGlobalDomain AppleShowAllExtensions -bool true + +# Finder: show status bar +defaults write com.apple.finder ShowStatusBar -bool true + +# Finder: allow text selection in Quick Look +defaults write com.apple.finder QLEnableTextSelection -bool true + +# Display full POSIX path as Finder window title +defaults write com.apple.finder _FXShowPosixPathInTitle -bool true + +# When performing a search, search the current folder by default +defaults write com.apple.finder FXDefaultSearchScope -string "SCcf" + +# Disable the warning when changing a file extension +defaults write com.apple.finder FXEnableExtensionChangeWarning -bool false + +# Enable spring loading for directories +defaults write NSGlobalDomain com.apple.springing.enabled -bool true + +# Remove the spring loading delay for directories +defaults write NSGlobalDomain com.apple.springing.delay -float 0.1 + +# Avoid creating .DS_Store files on network volumes +defaults write com.apple.desktopservices DSDontWriteNetworkStores -bool true + +# Enable snap-to-grid for icons on the desktop and in other icon views +/usr/libexec/PlistBuddy -c "Set :DesktopViewSettings:IconViewSettings:arrangeBy grid" ~/Library/Preferences/com.apple.finder.plist +/usr/libexec/PlistBuddy -c "Set :FK_StandardViewSettings:IconViewSettings:arrangeBy grid" ~/Library/Preferences/com.apple.finder.plist +/usr/libexec/PlistBuddy -c "Set :StandardViewSettings:IconViewSettings:arrangeBy grid" ~/Library/Preferences/com.apple.finder.plist + +# Set the size of icons on the desktop and in other icon views +/usr/libexec/PlistBuddy -c "Set :DesktopViewSettings:IconViewSettings:iconSize 64" ~/Library/Preferences/com.apple.finder.plist +/usr/libexec/PlistBuddy -c "Set :FK_StandardViewSettings:IconViewSettings:iconSize 64" ~/Library/Preferences/com.apple.finder.plist +/usr/libexec/PlistBuddy -c "Set :StandardViewSettings:IconViewSettings:iconSize 64" ~/Library/Preferences/com.apple.finder.plist + +# Use column view in all Finder windows by default +# Four-letter codes for the other view modes: `icnv`, `Nlsv`, `clmv`, `Flwv` +defaults write com.apple.finder FXPreferredViewStyle -string "clmv" + +# Show the ~/Library folder +chflags nohidden ~/Library + +############################################################################### +# Dock, Dashboard, and hot corners # +############################################################################### + +# Set the icon size of Dock items +defaults write com.apple.dock tilesize -int 30 + +# Speed up Mission Control animations +defaults write com.apple.dock expose-animation-duration -float 0.15 + +# Make Dock icons of hidden applications translucent +defaults write com.apple.dock showhidden -bool true + +# Enable the 'reduce transparency' option. Save GPU cycles. +defaults write com.apple.universalaccess reduceTransparency -bool true + +# Hot corners +# Possible values: +# 0: no-op +# 2: Mission Control +# 3: Show application windows +# 4: Desktop +# 5: Start screen saver +# 6: Disable screen saver +# 7: Dashboard +# 10: Put display to sleep +# 11: Launchpad +# 12: Notification Center +# Bottom right screen corner → Mission Control +defaults write com.apple.dock wvous-br-corner -int 2 +defaults write com.apple.dock wvous-br-modifier -int 0 +# Top left screen corner → Put display to sleep +defaults write com.apple.dock wvous-tl-corner -int 10 +defaults write com.apple.dock wvous-tl-modifier -int 0 +# Bottom left screen corner → Desktop +defaults write com.apple.dock wvous-bl-corner -int 4 +defaults write com.apple.dock wvous-bl-modifier -int 0 + +############################################################################### +# Safari & WebKit # +############################################################################### + +# Enable the Develop menu and the Web Inspector in Safari +defaults write com.apple.Safari IncludeDevelopMenu -bool true +defaults write com.apple.Safari WebKitDeveloperExtrasEnabledPreferenceKey -bool true +defaults write com.apple.Safari com.apple.Safari.ContentPageGroupIdentifier.WebKit2DeveloperExtrasEnabled -bool true + +# Add a context menu item for showing the Web Inspector in web views +defaults write NSGlobalDomain WebKitDeveloperExtras -bool true + +############################################################################### +# Mail # +############################################################################### + +# Copy email addresses as `foo@example.com` instead of `Foo Bar ` in Mail.app +defaults write com.apple.mail AddressesIncludeNameOnPasteboard -bool false + +############################################################################### +# Spotlight # +############################################################################### + +if [[ "$RUN_AS_ROOT" = true ]]; then + # Disable Spotlight indexing for any volume that gets mounted and has not yet + # been indexed before. + # Use `sudo mdutil -i off "/Volumes/foo"` to stop indexing any volume. + sudo defaults write /.Spotlight-V100/VolumeConfiguration Exclusions -array "/Volumes" + + # Restart spotlight + killall mds > /dev/null 2>&1 +fi + +############################################################################### +# Activity Monitor # +############################################################################### + +# Show the main window when launching Activity Monitor +defaults write com.apple.ActivityMonitor OpenMainWindow -bool true + +# Show all processes in Activity Monitor +defaults write com.apple.ActivityMonitor ShowCategory -int 0 + +############################################################################### +# Messages # +############################################################################### + +# Disable smart quotes as it’s annoying for messages that contain code +defaults write com.apple.messageshelper.MessageController SOInputLineSettings -dict-add "automaticQuoteSubstitutionEnabled" -bool false + +# Disable continuous spell checking +defaults write com.apple.messageshelper.MessageController SOInputLineSettings -dict-add "continuousSpellCheckingEnabled" -bool false + +############################################################################### +# App Store # +############################################################################### + +# Disable in-app rating requests from apps downloaded from the App Store. +defaults write com.apple.appstore InAppReviewEnabled -int 0 + +############################################################################### +# Kill/restart affected applications # +############################################################################### + +# Restart affected applications if `--no-restart` flag is not present. +if [[ ! ($* == *--no-restart*) ]]; then + for app in "cfprefsd" "Dock" "Finder" "Mail" "SystemUIServer" "Terminal"; do + killall "${app}" > /dev/null 2>&1 + done +fi + +printf "Please log out and log back in to make all settings take effect.\n" diff --git a/main.yml b/main.yml index 500c91766..6dda3a5e4 100644 --- a/main.yml +++ b/main.yml @@ -16,7 +16,10 @@ - role: elliotweiser.osx-command-line-tools - role: geerlingguy.mac.homebrew tags: ['homebrew'] - - role: geerlingguy.dotfiles + - role: mqz-mac-fonts + when: configure_fonts + tags: ['fonts'] + - role: mqz-dotfiles when: configure_dotfiles tags: ['dotfiles'] - role: geerlingguy.mac.mas @@ -31,7 +34,7 @@ when: configure_sudoers tags: ['sudoers'] - - import_tasks: tasks/terminal.yml + - import_tasks: tasks/iterm.yml when: configure_terminal tags: ['terminal'] diff --git a/requirements.yml b/requirements.yml index f5815ad80..04a6b460e 100644 --- a/requirements.yml +++ b/requirements.yml @@ -1,7 +1,8 @@ --- roles: - name: elliotweiser.osx-command-line-tools - - name: geerlingguy.dotfiles collections: - name: geerlingguy.mac + - name: community.general + version: "10.4" diff --git a/roles/mqz-dotfiles/defaults/main.yml b/roles/mqz-dotfiles/defaults/main.yml new file mode 100644 index 000000000..43725e07f --- /dev/null +++ b/roles/mqz-dotfiles/defaults/main.yml @@ -0,0 +1,18 @@ +--- +dotfiles_repo: "https://github.com/armarquez/dotfiles.git" +dotfiles_repo_version: "main" +dotfiles_repo_accept_hostkey: false +dotfiles_repo_local_destination: "~/dotfiles" + +dotfiles_home: "~" + +dotfiles_stow_modules: + - name: "zsh configuration" # Descriptive name for logging + package: "zsh" # The subdirectory in your dotfiles repo to link (e.g., /Users/anthony_marquez/dotfiles/zsh) + target_dir: "{{ dotfiles_home }}" + package_dir: "{{ dotfiles_repo_local_destination }}/zsh" # The full path to the module directory + package_state: "supress" + +zgenom_repo: "https://github.com/jandamm/zgenom.git" +zgenom_branch: "main" +zgenom_dest: "{{ dotfiles_home }}/.zgenom" diff --git a/roles/mqz-dotfiles/library/__init__.py b/roles/mqz-dotfiles/library/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/roles/mqz-dotfiles/library/stow.py b/roles/mqz-dotfiles/library/stow.py new file mode 100644 index 000000000..ca7b5fcfe --- /dev/null +++ b/roles/mqz-dotfiles/library/stow.py @@ -0,0 +1,312 @@ +#!/usr/bin/python3 + +''' + author : Caian R. Ertl + code : github.com/caian-org/ansible-stow + project : ansible-stow +description : An Ansible module that interacts with GNU Stow packages + license : CC0 (Public Domain) + +The person who associated a work with this deed has dedicated the work to the +public domain by waiving all of his or her rights to the work worldwide under +copyright law, including all related and neighboring rights, to the extent +allowed by law. + +You can copy, modify, distribute and perform the work, even for commercial +purposes, all without asking permission. + +AFFIRMER OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF +ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, +INCLUDING WITHOUT LIMITATION WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE, NON INFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER +DEFECTS, ACCURACY, OR THE PRESENT OR ABSENCE OF ERRORS, WHETHER OR NOT +DISCOVERABLE, ALL TO THE GREATEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW. + +For more information, please see + +''' + +import os +import re + +from ansible.module_utils.basic import AnsibleModule + + +STOW_CONFLICTS_MESSAGE_REGEX_BY_VERSION = { + '2.3.1': r'^\* existing target is neither a link nor a directory: (?P.+)$', + # pylint: disable-next=line-too-long + '2.4.0': r'^\* cannot stow (?P.+) over existing target (?P.+) since neither a link nor a directory and --adopt not specified$', + '2.4.1': r'^\* cannot stow (?P.+) over existing target (?P.+) since neither a link nor a directory and --adopt not specified$', +} + +SUPPORTED_STOW_VERSIONS = list(STOW_CONFLICTS_MESSAGE_REGEX_BY_VERSION.keys()) + + +def purge_conflicts(conflicted_files): + """Delete a file or unlink a symlink conflicting with a package. + + Args: + conflicted_files (list): Path of files or symlinks on the + filesystem that conflicts with package files. + + Returns: + dict or null: If the file is purged successfully, a None object is + returned. If something goes wrong, a dictionary is returned + containing the error message. + """ + + try: + for file in conflicted_files: + if os.path.islink(file): + os.unlink(file) + else: + os.remove(file) + + except Exception as err: # pylint: disable=broad-except + return {'message': f'unable to purge file "{file}"; error: {str(err)}'} + + return None + + +def is_stow_in_path(module): + """Verify if stow is installed and accessible (on the $PATH). + + Args: + module (AnsibleModule): The Ansible module object. + + Returns: + bool: True if stow is installed and accessible. False otherwise. + """ + + rc_, _, _ = module.run_command('which stow', check_rc=False) + return rc_ == 0 + + +def get_stow_version(module): + """Get the installed stow version. + + Args: + module (AnsibleModule): The Ansible module object. + + Returns: + str or null: The installed stow version in the format "X.Y.Z". If stow + is not installed or the version can't be determined, a None object + is returned. + """ + + rc_, stdout, _ = module.run_command('stow --version') + if rc_ != 0: + return None + + stow_version = re.compile(r'^stow \(GNU Stow\) version (?P\d+\.\d+\.\d+)$').match(stdout.strip()) + if not stow_version: + return None + + return stow_version.group('version') + + +def stow_has_conflicts(stow_version, module, package, cmd): + """Verify if a package has any conflicting files. + + Args: + stow_version (str): The installed stow version. + module (AnsibleModule): The Ansible module object. + package (str): The name of the package to be un/re/stowed. + cmd (str): The complete stow command, with all flags and arguments, to + be executed in dry-run mode (no change is made on the filesystem). + + Returns: + dict or null: If no conflict is found, a None object is returned. + If a conflict is found, a dictionary is returned. + + Recoverable (i.e., conflicts on pre-existing files and symlinks on + the filesystem) conflicts returns a different dict from a + non-recoverable one: + + { + 'recoverable': True, + 'message': '...', + 'files': ['/home/user/.bashrc', '/home/user/.config/foo'] + } + + --- + + { + 'recoverable': False, + 'message': '...' + } + """ + + params = module.params + + # Dry-run to check if there's any conflict. + cmd = f'{cmd} --no' + rc_, _, stderr = module.run_command(cmd) + + if rc_ == 0: + return None + + # Return code 2 means that the package points to a path that has a + # directory on it. E.g.: + # + # Package "foo" is meant to be stowed at the home directory at + # ".config/foo/bar" (absolute path being "/home/user/.config/foo/bar"). + # + # If "bar" already exists as a directory at "/home/user/.config/foo", + # stow can't continue. + # + # One way of dealing with this situation would be removing the directory. + # Another way would be by backuping it. Either way, it's risky to + # recursively delete an entire directory or even move it. + # + # This kind of scenario should be handled manually by the user, hence + # the function returns with a non-recoverable flag error. + if rc_ == 2: + return {'recoverable': False, 'message': ''} + + conflict_re = re.compile(STOW_CONFLICTS_MESSAGE_REGEX_BY_VERSION[stow_version]) + + conflicts = [] + for sel in stderr.split('\n'): + conflict_match = conflict_re.match(sel.strip()) + + if conflict_match: + conflicts.append(os.path.join(params['target'], conflict_match.group('link_path'))) + + conff = ', '.join(f'"{f}"' for f in conflicts) + msg = f'unable to stow package "{package}" to "{params["target"]}"; conflicted files: {conff}' + + return {'recoverable': True, 'message': msg, 'files': conflicts} + + +def has_stow_changed_links(stow_output): + if stow_output.strip() == '': + return 0 + + n_linked = set() + link_re = re.compile(r'^LINK: (?P.+) => (?P.+)(?P \(reverts previous action\))?$') + + n_unlinked = set() + unlink_re = re.compile(r'^UNLINK: (?P.+)$') + + for sel in stow_output.split('\n'): + sel = sel.strip() + + link_match = link_re.match(sel) + if link_match: + n_linked.add(link_match.group('link_path')) + continue + + unlink_match = unlink_re.match(sel) + if unlink_match: + n_unlinked.add(unlink_match.group('link_path')) + + return n_linked != n_unlinked + + +def stow(stow_version, module, package, state): + """Perform stow on a package against the filesystem. + + Args: + stow_version (str): The installed stow version. + module (AnsibleModule): The Ansible module object. + package (str): The name of the package to be un/re/stowed. + state (str): The desirable package state within the system. + + Returns: + dict: A dictionary that contains an error flag, the returned message + and wether something changed or not. + """ + + params = module.params + + flag = '' + if state in ('present', 'supress'): + flag = '--stow' + + elif state == 'absent': + flag = '--delete' + + elif state == 'latest': + flag = '--restow' + + cmd = f'stow {flag} {package} --target={params["target"]} --dir={params["dir"]} --verbose' + + conflict = stow_has_conflicts(stow_version, module, package, cmd) + if conflict: + if state != 'supress' or not conflict['recoverable']: + return {'error': True, 'message': conflict['message']} + + err = purge_conflicts(conflict['files']) + if err: + return {'error': True, 'message': err['message']} + + # When increasing verbosity level with the "--verbose" flag, all output + # will be sent to the standard error (stderr). + # + # Stow is, by itself, an idempotent tool. + # If a given package is already stowed, the tool will not perform again. + # If a package is succesfully stowed, stow will output what have been done. + # + # That's why "information on stderr" equals "something has changed" + # (supposing execution passed all errors checking). + rc_, _, se_ = module.run_command(cmd) + if rc_ != 0: + msg = f'execution of command "{cmd}" failed with error code {rc_}; output: "{se_}"' + return {'error': True, 'message': msg} + + return {'error': False, 'changed': has_stow_changed_links(se_)} + + +def main(): + '''The module main routine.''' + + module = AnsibleModule( + argument_spec={ + 'dir': { + 'required': True, + 'type': 'str', + }, + 'package': { + 'required': True, + 'type': 'list', + }, + 'target': { + 'required': False, + 'type': 'str', + 'default': os.environ.get('HOME'), + }, + 'state': { + 'required': True, + 'type': 'str', + 'choices': ['absent', 'present', 'latest', 'supress'], + }, + } + ) + + params = module.params + + if not is_stow_in_path(module): + module.fail_json(msg='unable to find stow') + + stow_version = get_stow_version(module) + if not stow_version: + module.fail_json(msg='unable to determine stow version') + + if stow_version not in SUPPORTED_STOW_VERSIONS: + module.fail_json(msg=f'stow version {stow_version} is not supported') + + has_changed = False + + for package in list(params['package']): + ret = stow(stow_version, module, package, params['state']) + if ret['error']: + module.fail_json(msg=ret['message']) + + has_changed = has_changed or ret['changed'] + + module.exit_json(changed=has_changed) + + +if __name__ == '__main__': + main() diff --git a/roles/mqz-dotfiles/tasks/main.yml b/roles/mqz-dotfiles/tasks/main.yml new file mode 100644 index 000000000..7049234b6 --- /dev/null +++ b/roles/mqz-dotfiles/tasks/main.yml @@ -0,0 +1,30 @@ +--- +- name: Setup Zgenom + include_tasks: setup-zgenom.yml + +- name: Ensure dotfiles repository is cloned locally. + ansible.builtin.git: + repo: "{{ dotfiles_repo }}" + dest: "{{ dotfiles_repo_local_destination }}" + version: "{{ dotfiles_repo_version }}" + accept_hostkey: "{{ dotfiles_repo_accept_hostkey }}" + become: false + +- name: Ensure Stow is installed (example for macOS/Ubuntu) + ansible.builtin.package: + name: stow + state: present + # TODO: Add conditional logic here for different OSes, e.g.: + # when: ansible_os_family == 'Debian' or ansible_os_family == 'RedHat' or ansible_distribution == 'MacOSX' + +- name: Stow dotfiles modules into target directories. + stow: + package: "{{ item.package}}" + target: "{{ item.target_dir }}" + dir: "{{ item.package_dir }}" + state: "{{ item.package_state | default('present') }}" + become: false + loop: "{{ dotfiles_stow_modules }}" + loop_control: + loop_var: item + label: "Stowing '{{ item.name }}' from '{{ item.package_dir }}' to '{{ item.target_dir }}'" diff --git a/roles/mqz-dotfiles/tasks/setup-zgenom.yml b/roles/mqz-dotfiles/tasks/setup-zgenom.yml new file mode 100644 index 000000000..f8bca0bf2 --- /dev/null +++ b/roles/mqz-dotfiles/tasks/setup-zgenom.yml @@ -0,0 +1,32 @@ +--- +- name: Install Zsh (if not present) + package: + name: zsh + state: present + become: true + when: ansible_os_family != 'Darwin' + +- name: Clone zgenom + ansible.builtin.git: + repo: "{{ zgenom_repo }}" + version: "{{ zgenom_branch }}" + dest: "{{ zgenom_dest }}" + clone: yes + update: yes + become: false + register: zgenom_operation + retries: 3 + delay: 5 + until: zgenom_operation is succeeded + +- name: Show what would happen in check mode + debug: + msg: "Would clone zgenom to {{ zgenom_dest }}" + when: ansible_check_mode and zgenom_operation.changed + +- name: Verify zgenom installation + stat: + path: "{{ zgenom_dest }}/zgenom.zsh" + register: zgenom_verify + failed_when: not zgenom_verify.stat.exists + when: ansible_check_mode == false diff --git a/roles/mqz-mac-fonts/defaults/main.yml b/roles/mqz-mac-fonts/defaults/main.yml new file mode 100644 index 000000000..f7e13f055 --- /dev/null +++ b/roles/mqz-mac-fonts/defaults/main.yml @@ -0,0 +1,7 @@ +fonts_to_install: + - name: "MesloLGS NF" # A human-readable name for the font + zip_url: "https://github.com/ryanoasis/nerd-fonts/releases/download/v3.4.0/Meslo.zip" + checksum: "sha256:13b502ac8c2bd9d3161018064560e23cd42b175bb730780a270975265a19ad57" + # Optional overrides: + # install_path: "/custom/font/path" # Override default_font_install_path if needed + # extract_pattern: "*.ttf,*.otf" # Override default find pattern if needed diff --git a/roles/mqz-mac-fonts/tasks/install_font.yml b/roles/mqz-mac-fonts/tasks/install_font.yml new file mode 100644 index 000000000..260f7b01a --- /dev/null +++ b/roles/mqz-mac-fonts/tasks/install_font.yml @@ -0,0 +1,110 @@ +--- +- name: Set font-specific variables for {{ font_item.name }}. + ansible.builtin.set_fact: + current_font_download_path: "{{ ansible_env.HOME }}/Downloads/{{ font_item.name | replace(' ', '_') | lower }}_font.zip" + current_font_extract_path: "{{ ansible_env.HOME }}/Downloads/{{ font_item.name | replace(' ', '_') | lower }}_extracted_font" + current_font_install_path: "{{ font_item.install_path | default(default_font_install_path) | trim }}" + current_font_extract_pattern: "{{ font_item.extract_pattern | default('*.ttf,*.otf') }}" + +- name: Ensure the specific font's target installation directory exists (if it differs from the default). + ansible.builtin.file: + path: "{{ current_font_install_path }}" + state: directory + mode: '0755' + when: current_font_install_path != (default_font_install_path | trim) + become: false + +- name: Download {{ font_item.name }} font zip file. + ansible.builtin.get_url: + url: "{{ font_item.zip_url }}" + dest: "{{ current_font_download_path }}" + mode: '0644' + checksum: "{{ font_item.checksum }}" + become: false + +- name: Ensure extraction directory for {{ font_item.name }} is clean. + ansible.builtin.file: + path: "{{ current_font_extract_path }}" + state: absent + become: false + changed_when: false + +- name: Create fresh extraction directory for {{ font_item.name }}. + ansible.builtin.file: + path: "{{ current_font_extract_path }}" + state: directory + mode: '0755' + become: false + +- name: Handle font extraction based on mode + block: + - name: Unarchive the {{ font_item.name }} zip file (normal mode). + ansible.builtin.unarchive: + src: "{{ current_font_download_path }}" + dest: "{{ current_font_extract_path }}" + remote_src: true + become: false + when: not ansible_check_mode + + - name: Simulate extraction in check mode + ansible.builtin.debug: + msg: | + CHECK MODE: Would extract {{ font_item.name }} + Source: {{ current_font_download_path }} + Destination: {{ current_font_extract_path }} + when: ansible_check_mode + +- name: Find font files or simulate in check mode + block: + - name: Find font files ({{ current_font_extract_pattern }}) in the extracted directory for {{ font_item.name }}. + ansible.builtin.find: + paths: "{{ current_font_extract_path }}" + patterns: "{{ current_font_extract_pattern }}" + recurse: true + register: found_font_files_for_item + become: false + when: not ansible_check_mode + + - name: Create mock font file list for check mode + ansible.builtin.set_fact: + found_font_files_for_item: + files: + - path: "{{ current_font_extract_path }}/{{ font_item.name | replace(' ', '') }}-Regular.ttf" + - path: "{{ current_font_extract_path }}/{{ font_item.name | replace(' ', '') }}-Bold.ttf" + - path: "{{ current_font_extract_path }}/{{ font_item.name | replace(' ', '') }}-Italic.ttf" + when: ansible_check_mode + +- name: Process font files based on mode + block: + - name: Copy {{ font_item.name }} font files to the installation directory. + ansible.builtin.copy: + src: "{{ font_file.path }}" + dest: "{{ current_font_install_path }}/{{ font_file.path | basename }}" + mode: '0644' + become: false + loop: "{{ found_font_files_for_item.files }}" + loop_control: + loop_var: font_file + when: not ansible_check_mode + + - name: Show what would be installed (check mode). + ansible.builtin.debug: + msg: | + CHECK MODE: Would install font files for {{ font_item.name }}: + {% for font_file in found_font_files_for_item.files %} + - {{ font_file.path | basename }} → {{ current_font_install_path }}/{{ font_file.path | basename }} + {% endfor %} + when: ansible_check_mode + +- name: Clean up temporary files + ansible.builtin.file: + path: "{{ item_cleanup_path }}" + state: absent + become: false + loop: + - "{{ current_font_download_path }}" + - "{{ current_font_extract_path }}" + loop_control: + loop_var: item_cleanup_path + ignore_errors: true + when: not ansible_check_mode diff --git a/roles/mqz-mac-fonts/tasks/main.yml b/roles/mqz-mac-fonts/tasks/main.yml new file mode 100644 index 000000000..819a0631b --- /dev/null +++ b/roles/mqz-mac-fonts/tasks/main.yml @@ -0,0 +1,38 @@ +--- +- name: Set default font installation path based on OS. + ansible.builtin.set_fact: + default_font_install_path: >- + {%- if ansible_os_family == 'Darwin' -%} + {{ ansible_env.HOME }}/Library/Fonts + {%- elif ansible_os_family == 'Debian' or ansible_os_family == 'RedHat' or ansible_os_family == 'Archlinux' -%} + {{ ansible_env.HOME }}/.local/share/fonts + {%- else -%} + {{ ansible_env.HOME }}/.fonts + {%- endif -%} + +- name: Ensure the default font installation directory exists. + ansible.builtin.file: + path: "{{ default_font_install_path }}" + state: directory + mode: '0755' + become: false + +- name: Process each font for installation + ansible.builtin.include_tasks: install_font.yml + loop: "{{ fonts_to_install }}" + loop_control: + loop_var: font_item + label: "Installing {{ font_item.name }}" + +- name: Notify user about font cache refresh. + ansible.builtin.debug: + msg: | + All configured fonts have been copied. + You might need to refresh your font cache or restart applications (e.g., terminal, IDEs) + for the new fonts to appear in menus. + + On Linux, you can typically refresh the font cache by running: + fc-cache -fv + + On macOS, opening 'Font Book' and letting it validate fonts can sometimes help, + or simply logging out and back in. diff --git a/tasks/iterm.yml b/tasks/iterm.yml new file mode 100644 index 000000000..b97418954 --- /dev/null +++ b/tasks/iterm.yml @@ -0,0 +1,32 @@ +--- +- name: Ensure the iTerm2 DynamicProfiles directory exists. + ansible.builtin.file: + path: "{{ iterm2_dynamic_profiles_dest_dir }}" + state: directory + mode: '0755' + become: false + +- name: Copy iTerm2 profile JSON files to the DynamicProfiles directory. + ansible.builtin.copy: + src: "{{ iterm2_profiles_source_dir }}/" # The trailing slash copies contents of the directory + dest: "{{ iterm2_dynamic_profiles_dest_dir }}" + mode: '0644' # Standard permissions for config files + become: false + # Consider adding a handler here to restart iTerm2 or notify the user + # if we want immediate effect, though iTerm2 usually picks up changes quickly. + +- name: Find existing dynamic profile files not in source. + ansible.builtin.find: + paths: "{{ iterm2_dynamic_profiles_dest_dir }}" + patterns: "*.json" # Look for JSON files + use_regex: true + register: existing_dynamic_profiles + become: false + +- name: Remove old iTerm2 dynamic profile files not present in source. + ansible.builtin.file: + path: "{{ item.path }}" + state: absent + when: item.path | basename not in (lookup('fileglob', iterm2_profiles_source_dir + '/*.json') | map('basename') | list) + loop: "{{ existing_dynamic_profiles.files }}" + become: false diff --git a/tasks/osx.yml b/tasks/osx.yml index ec12cd524..44ff028cf 100644 --- a/tasks/osx.yml +++ b/tasks/osx.yml @@ -1,4 +1,11 @@ --- +- name: Copy OSX configuration script files to the appropriate directory. + ansible.builtin.copy: + src: "{{ osx_script_source }}" + dest: "{{ osx_script_dest }}" + mode: '0644' + become: false + # TODO: Use sudo once .osx can be run via root with no user interaction. - name: Run .osx dotfiles. command: "{{ osx_script }}" diff --git a/tasks/terminal.yml b/tasks/terminal.yml index b1a6d6e8c..e43b6c50d 100644 --- a/tasks/terminal.yml +++ b/tasks/terminal.yml @@ -9,21 +9,21 @@ - name: Ensure custom Terminal profile is added. copy: - src: files/terminal/JJG-Term.terminal - dest: /tmp/JJG-Term.terminal + src: files/terminal/boogey-Term.terminal + dest: /tmp/boogey-Term.terminal changed_when: false - when: "'JJG-Term' not in terminal_theme.stdout" + when: "'boogey-Term' not in terminal_theme.stdout" - name: Ensure custom Terminal profile is added. - command: open /tmp/JJG-Term.terminal + command: open /tmp/boogey-Term.terminal changed_when: false - when: "'JJG-Term' not in terminal_theme.stdout" + when: "'boogey-Term' not in terminal_theme.stdout" # TODO: This doesn't work in Yosemite. Consider a different solution? - name: Ensure custom Terminal profile is set as default. command: "{{ item }}" with_items: - - defaults write com.apple.terminal 'Default Window Settings' -string JJG-Term - - defaults write com.apple.terminal 'Startup Window Settings' -string JJG-Term + - defaults write com.apple.terminal 'Default Window Settings' -string boogey-Term + - defaults write com.apple.terminal 'Startup Window Settings' -string boogey-Term changed_when: false - when: "'JJG-Term' not in terminal_theme.stdout" + when: "'boogey-Term' not in terminal_theme.stdout"