[meta] Switch to using a module for defaults

Defaults module taken from ansible/ansible-modules-extras/pull/64.

Fixes #10.
pull/28/head
Alpha Chen 10 years ago
parent 315eeef7f8
commit 7b651ddbab

@ -0,0 +1,349 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2014, GeekChimp - Franck Nijhof <franck@geekchimp.com>
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
DOCUMENTATION = '''
---
module: osx_defaults
author: Franck Nijhof
short_description: osx_defaults allows users to read, write, and delete Mac OS X user defaults from Ansible
description:
- osx_defaults allows users to read, write, and delete Mac OS X user defaults from Ansible scripts.
Mac OS X applications and other programs use the defaults system to record user preferences and other
information that must be maintained when the applications aren't running (such as default font for new
documents, or the position of an Info panel).
version_added: 1.8
options:
domain:
description:
- The domain is a domain name of the form com.companyname.appname.
required: false
default: NSGlobalDomain
key:
description:
- The key of the user preference
required: true
type:
description:
- The type of value to write.
required: false
default: string
choices: [ "array", "bool", "boolean", "date", "float", "int", "integer", "string" ]
array_add:
description:
- Add new elements to the array for a key which has an array as its value.
required: false
default: string
choices: [ "true", "false" ]
value:
description:
- The value to write. Only required when state = present.
required: false
default: null
state:
description:
- The state of the user defaults
required: false
default: present
choices: [ "present", "absent" ]
notes:
- Apple Mac caches defaults. You may need to logout and login to apply the changes.
'''
EXAMPLES = '''
- osx_defaults: domain=com.apple.Safari key=IncludeInternalDebugMenu type=bool value=true state=present
- osx_defaults: domain=NSGlobalDomain key=AppleMeasurementUnits type=string value=Centimeters state=present
- osx_defaults: key=AppleMeasurementUnits type=string value=Centimeters
- osx_defaults:
key: AppleLanguages
type: array
value: ["en", "nl"]
- osx_defaults: domain=com.geekchimp.macable key=ExampleKeyToRemove state=absent
'''
from datetime import datetime
# exceptions --------------------------------------------------------------- {{{
class OSXDefaultsException(Exception):
pass
# /exceptions -------------------------------------------------------------- }}}
# class MacDefaults -------------------------------------------------------- {{{
class OSXDefaults(object):
""" Class to manage Mac OS user defaults """
# init ---------------------------------------------------------------- {{{
""" Initialize this module. Finds 'defaults' executable and preps the parameters """
def __init__(self, **kwargs):
# Initial var for storing current defaults value
self.current_value = None
# Just set all given parameters
for key, val in kwargs.iteritems():
setattr(self, key, val)
# Try to find the defaults executable
self.executable = self.module.get_bin_path(
'defaults',
required=False,
opt_dirs=self.path.split(':'),
)
if not self.executable:
raise OSXDefaultsException("Unable to locate defaults executable.")
# When state is present, we require a parameter
if self.state == "present" and self.value is None:
raise OSXDefaultsException("Missing value parameter")
# Ensure the value is the correct type
self.value = self._convert_type(self.type, self.value)
# /init --------------------------------------------------------------- }}}
# tools --------------------------------------------------------------- {{{
""" Converts value to given type """
def _convert_type(self, type, value):
if type == "string":
return str(value)
elif type in ["bool", "boolean"]:
if value.lower() in [True, 1, "true", "1", "yes"]:
return True
elif value.lower() in [False, 0, "false", "0", "no"]:
return False
raise OSXDefaultsException("Invalid boolean value: {0}".format(repr(value)))
elif type == "date":
try:
return datetime.strptime(value.split("+")[0].strip(), "%Y-%m-%d %H:%M:%S")
except ValueError:
raise OSXDefaultsException(
"Invalid date value: {0}. Required format yyy-mm-dd hh:mm:ss.".format(repr(value))
)
elif type in ["int", "integer"]:
if not str(value).isdigit():
raise OSXDefaultsException("Invalid integer value: {0}".format(repr(value)))
return int(value)
elif type == "float":
try:
value = float(value)
except ValueError:
raise OSXDefaultsException("Invalid float value: {0}".format(repr(value)))
return value
elif type == "array":
if not isinstance(value, list):
raise OSXDefaultsException("Invalid value. Expected value to be an array")
return value
raise OSXDefaultsException('Type is not supported: {0}'.format(type))
""" Converts array output from defaults to an list """
@staticmethod
def _convert_defaults_str_to_list(value):
# Split output of defaults. Every line contains a value
value = value.splitlines()
# Remove first and last item, those are not actual values
value.pop(0)
value.pop(-1)
# Remove extra spaces and comma (,) at the end of values
value = [re.sub(',$', '', x.strip(' ')) for x in value]
return value
# /tools -------------------------------------------------------------- }}}
# commands ------------------------------------------------------------ {{{
""" Reads value of this domain & key from defaults """
def read(self):
# First try to find out the type
rc, out, err = self.module.run_command([self.executable, "read-type", self.domain, self.key])
# If RC is 1, the key does not exists
if rc == 1:
return None
# If the RC is not 0, then terrible happened! Ooooh nooo!
if rc != 0:
raise OSXDefaultsException("An error occurred while reading key type from defaults: " + out)
# Ok, lets parse the type from output
type = out.strip().replace('Type is ', '')
# Now get the current value
rc, out, err = self.module.run_command([self.executable, "read", self.domain, self.key])
# Strip output
out = out.strip()
# An non zero RC at this point is kinda strange...
if rc != 0:
raise OSXDefaultsException("An error occurred while reading key value from defaults: " + out)
# Convert string to list when type is array
if type == "array":
out = self._convert_defaults_str_to_list(out)
# Store the current_value
self.current_value = self._convert_type(type, out)
""" Writes value to this domain & key to defaults """
def write(self):
# We need to convert some values so the defaults commandline understands it
if type(self.value) is bool:
value = "TRUE" if self.value else "FALSE"
elif type(self.value) is int or type(self.value) is float:
value = str(self.value)
elif self.array_add and self.current_value is not None:
value = list(set(self.value) - set(self.current_value))
elif isinstance(self.value, datetime):
value = self.value.strftime('%Y-%m-%d %H:%M:%S')
else:
value = self.value
# When the type is array and array_add is enabled, morph the type :)
if self.type == "array" and self.array_add:
self.type = "array-add"
# All values should be a list, for easy passing it to the command
if not isinstance(value, list):
value = [value]
rc, out, err = self.module.run_command([self.executable, 'write', self.domain, self.key, '-' + self.type] + value)
if rc != 0:
raise OSXDefaultsException('An error occurred while writing value to defaults: ' + out)
""" Deletes defaults key from domain """
def delete(self):
rc, out, err = self.module.run_command([self.executable, 'delete', self.domain, self.key])
if rc != 0:
raise OSXDefaultsException("An error occurred while deleting key from defaults: " + out)
# /commands ----------------------------------------------------------- }}}
# run ----------------------------------------------------------------- {{{
""" Does the magic! :) """
def run(self):
# Get the current value from defaults
self.read()
# Handle absent state
if self.state == "absent":
print "Absent state detected!"
if self.current_value is None:
return False
self.delete()
return True
# There is a type mismatch! Given type does not match the type in defaults
if self.current_value is not None and type(self.current_value) is not type(self.value):
raise OSXDefaultsException("Type mismatch. Type in defaults: " + type(self.current_value).__name__)
# Current value matches the given value. Nothing need to be done. Arrays need extra care
if self.type == "array" and self.current_value is not None and not self.array_add and \
set(self.current_value) == set(self.value):
return False
elif self.type == "array" and self.current_value is not None and self.array_add and \
len(list(set(self.value) - set(self.current_value))) == 0:
return False
elif self.current_value == self.value:
return False
# Change/Create/Set given key/value for domain in defaults
self.write()
return True
# /run ---------------------------------------------------------------- }}}
# /class MacDefaults ------------------------------------------------------ }}}
# main -------------------------------------------------------------------- {{{
def main():
module = AnsibleModule(
argument_spec=dict(
domain=dict(
default="NSGlobalDomain",
required=False,
),
key=dict(
default=None,
),
type=dict(
default="string",
required=False,
choices=[
"array",
"bool",
"boolean",
"date",
"float",
"int",
"integer",
"string",
],
),
array_add=dict(
default=False,
required=False,
choices=BOOLEANS,
),
value=dict(
default=None,
required=False,
),
state=dict(
default="present",
required=False,
choices=[
"absent", "present"
],
),
path=dict(
default="/usr/bin:/usr/local/bin",
required=False,
)
),
supports_check_mode=True,
)
domain = module.params['domain']
key = module.params['key']
type = module.params['type']
array_add = module.params['array_add']
value = module.params['value']
state = module.params['state']
path = module.params['path']
try:
defaults = OSXDefaults(module=module, domain=domain, key=key, type=type,
array_add=array_add, value=value, state=state, path=path)
changed = defaults.run()
module.exit_json(changed=changed)
except OSXDefaultsException as e:
module.fail_json(msg=e.message)
# /main ------------------------------------------------------------------- }}}
from ansible.module_utils.basic import *
main()

@ -1,10 +1,16 @@
---
- include_vars: defaults.yml
- command: defaults write {{ item }}
# - command: defaults write {{ item }}
- osx_defaults:
domain: "{{ item.domain | default(omit) }}"
key: "{{ item.key }}"
type: "{{ item.type }}"
value: "{{ item.value }}"
with_items: osx_defaults
- command: defaults -currentHost write NSGlobalDomain com.apple.mouse.tapBehavior -bool true # tap to click
# - command: defaults -currentHost write NSGlobalDomain com.apple.mouse.tapBehavior -bool true # tap to click
# - command: defaults write {{ item }} dontAutoLoad -array
# /System/Library/CoreServices/Menu Extras/TimeMachine.menu
# /System/Library/CoreServices/Menu Extras/Bluetooth.menu
@ -16,8 +22,8 @@
# - Set :FK_StandardViewSettings:IconViewSettings:arrangeBy grid
# - Set :StandardViewSettings:IconViewSettings:arrangeBy grid
- command: killall {{ item }}
with_items:
- Finder
- Dock
- SystemUIServer
# - command: killall {{ item }}
# with_items:
# - Finder
# - Dock
# - SystemUIServer

@ -5,6 +5,7 @@
- include: casks.yml
- include: fonts.yml
- include: defaults.yml
tags: defaults
- file: path=~/Library/KeyBindings state=directory
- file:

@ -3,36 +3,146 @@ osx_defaults:
# This is named `osx_defaults` since there's an Ansible conflict when this key
# is named just `defaults`.
- -g NSDisableAutomaticTermination -bool true # Don't quit idle applications
- NSGlobalDomain AppleFontSmoothing -int 2 # subpixel rendering on non-Apple LCDs
- NSGlobalDomain AppleKeyboardUIMode -int 3 # full keyboard access
- NSGlobalDomain AppleShowAllExtensions -bool true # show all extensions by default
- NSGlobalDomain KeyRepeat -int 2 # keyboard repeat rate
- NSGlobalDomain IinitialKeyRepeat -int 15 # delay before keyboard repeat
- NSGlobalDomain NSTableViewDefaultSizeMode -int 1 # set sidebar item size to small
- NSGlobalDomain NSQuitAlwaysKeepsWindows -bool false # disable resume
- NSGlobalDomain WebKitDeveloperExtras -bool true # add debug menu in web views
- NSGlobalDomain com.apple.mouse.tapBehavior -bool true # tap to click
- com.apple.Safari IncludeInternalDebugMenu -bool true # enable Debug menu in Safari
- com.apple.dashboard mcx-disabled -bool true # disable dashboard
- com.apple.desktopservices DSDontWriteNetworkStores true # don't write .DS_Store to network volumes
- com.apple.dock autohide -bool true # automatically hide and show the dock
- com.apple.dock mineffect -string scale # minimize windows using the scale effect
- com.apple.dock mru-spaces -bool false # don't rearrange spaces
- com.apple.dock orientation -string left
- com.apple.dock wvous-bl-corner -int 10 # set the bottom left hot corner to sleep the display
- com.apple.driver.AppleBluetoothMultitouch.trackpad Clicking -bool true
- com.apple.driver.AppleBluetoothMultitouch.trackpad DragLock -int 1
- com.apple.driver.AppleBluetoothMultitouch.trackpad Dragging -int 1
- com.apple.dt.Xcode DVTTextAutoSuggestCompletionsDelay 0.1 # Xcode autocomplete delay
- com.apple.finder FXEnableExtensionChangeWarning -bool false # don't ask when changing file extension
- com.apple.finder FXPreferredViewStyle -string Nlsv # default to list view
- com.apple.finder QLEnableTextSelection -bool true # enable text selection in QuickLook
- com.apple.finder _FXShowPosixPathInTitle -bool true # show full path in Finder
- com.apple.screencapture disable-shadow -bool true # no window shadows when capturing windows
- com.apple.screencapture location ~/Downloads
- com.apple.screensaver askForPassword -int 1
- com.google.Chrome AppleEnableSwipeNavigateWithScrolls -bool false
# NSGlobalDomain defaults
- # don't quit idle applications
domain: -g
key: NSDisableAutomaticTermination
type: bool
value: true
- # subpixel rendering on non-Apple LCDs
key: AppleFontSmoothing
type: int
value: 2
- # full keyboard access
key: AppleKeyboardUIMode
type: int
value: 3
- # show all extensions by default
key: AppleShowAllExtensions
type: bool
value: true
- # keyboard repeat rate
key: KeyRepeat
type: int
value: 2
- # delay before keyboard repeat
key: InitialKeyRepeat
type: int
value: 15
- # set sidebar item size to small
key: NSTableViewDefaultSizeMode
type: int
value: 1
- # disable resume
key: NSQuitAlwaysKeepsWindows
type: bool
value: false
- # add debug menu in web views
key: WebKitDeveloperExtras
type: bool
value: true
- # tap to click
key: com.apple.mouse.tapBehavior
type: bool
value: true
- # enable Debug menu in Safari
domain: com.apple.Safari
key: IncludeInternalDebugMenu
type: bool
value: true
- # disable dashboard
domain: com.apple.dashboard
key: mcx-disabled
type: bool
value: true
- # don't write .DS_Store to network volumes
domain: com.apple.desktopservices
key: DSDontWriteNetworkStores
type: bool
value: true
# Dock defaults
- # automatically hide and show the dock
domain: com.apple.dock
key: autohide
type: bool
value: true
- # minimize windows using the scale effect
domain: com.apple.dock
key: mineffect
type: string
value: scale
- # don't rearrange spaces
domain: com.apple.dock
key: mru-spaces
type: bool
value: false
- domain: com.apple.dock
key: orientation
type: string
value: left
- # set the bottom left hot corner to sleep the display
domain: com.apple.dock
key: wvous-bl-corner
type: int
value: 10
# Trackpad settings
- domain: com.apple.driver.AppleBluetoothMultitouch.trackpad
key: Clicking
type: bool
value: true
- domain: com.apple.driver.AppleBluetoothMultitouch.trackpad
key: DragLock
type: int
value: 1
- domain: com.apple.driver.AppleBluetoothMultitouch.trackpad
key: Dragging
type: int
value: 1
# Finder defaults
- # don't ask when changing file extension
domain: com.apple.finder
key: FXEnableExtensionChangeWarning
type: bool
value: false
- # default to list view
domain: com.apple.finder
key: FXPreferredViewStyle
type: string
value: Nlsv
- # enable text selection in QuickLook
domain: com.apple.finder
key: QLEnableTextSelection
type: bool
value: true
- # show full path in Finder
domain: com.apple.finder
key: _FXShowPosixPathInTitle
type: bool
value: true
- # no window shadows when capturing windows
domain: com.apple.screencapture
key: disable-shadow
type: bool
value: true
- domain: com.apple.screencapture
key: location
type: string
value: ~/Downloads
- domain: com.apple.screensaver
key: askForPassword
type: int
value: 1
- domain: com.google.Chrome
key: AppleEnableSwipeNavigateWithScrolls
type: bool
value: false

Loading…
Cancel
Save