Release plist and macos_user resources, new Xcode libraries, test cookbook and documentation. (#24)
* first commit plistbuddy
* all recipe/plist in macos
* extract plistbuddy to resource
* Straighten out unit tests
- Correctly include modules
- Correctly setup spec_helper
- Correct unit test / respec setup
* first passing test
* add more data type tests
* Most data types passing
- Boolean values are working
- Array and data type values are not
- PlistBuddy "commands" (add, print, etc.) are passing
* More testing
- Skip Array and Dict types as their implementation will be more
complicated
- Add test for float type
- Put the correct type in the test description
- Cookstyle changes
* PlistBuddy and testing update
- Add separate test cookbook for PlistBuddy resource
- Add kitchen.yml for the above purpose
- Modify and update plistbuddy resource/helpers
* remove extra newline
* add metadata file
* edit metadata; add Berksfile
* move Berksfile to correct location
* add testing requirements for berks vendor
* move kitchen file
* Converge passing in test cookbook
- Opportunistic cleanup of xcode test
- some temp debugging info in resource definition
- Rename name_property to command so as to not override action
* Itempotence
- Removed 'bool' prefix from boolean types, as that was somehow causing
issues
- Changed name_property to the path and removed that property
- Use actual actions in the resource for the PlistBuddy commands
* Fix false positive?
- Not sure how the previous test was passing previously, but I adjusted
the boolean value for the BazEntry test so that the signature input
matches the output
* Add logic for adding a value
- The data type should only be used when adding a value to a plist.
Otherwise, we don't need to use the data type
* remove debugging; fix smoke tests
* remove .kitchen.yml
* bump version to 0.8.5
* unskip ChefSpec tests - they're passing
* mark args_formatter as private method
* Feature/macos user (#14)
* first commit add_user recipe
* Testing Refactor & macos_user resource
- Create new testing cookbook that contains tests for both plistbuddy
and macos_user resources. The hope here is to have a single testing
cookbook that contains multiple suites and suite testing variations
- Create new macos_user resource - contains single boolean 'true'
settings for autologin and admin user. Still work to be done for admin
user and all steps that need to be taken to acheive the task.
* fix method call
* fix another method call
* user native ruby File class and not overriden
* fix some minor issues with resource; add kcpassword file
* Add OG Ruby kcpassword file helper module
* slightly different approach for Kcpassword module
* passing test
* Fix up kcpassword library module, smoke tests
- Modify Kcpassword helper module to use a method for
magic bits. This isn't ideal, but it works for now
- Update smoke tests so that john_jr's group ID is 20. This
may not be correct, but setting as 20 until it is further
investigated
* opportunistic refactor of keep_awake recipe
* Refactor testing cookbook layout
- Consolodate testing suites into single default suite
- Add tests for keep_awake recipe
- Use more common examples to test PlistBuddy resource
* move xcode test to test cookbook
* Opportunistic bug fix for plistbuddy resource
- Previously, the plistbuddy resource did not allow
any keys with spaces unless they were used in the value itself.
Now, you can use keys with spaces in the value property
without needing to double-quote it
- Also, fix the PlistBuddy executable string to be correct
* revert kcpassword.rb helper to a671164
* fix incorrect PlistBuddy data type name
* Fix kcpassword.rb and update plistbuddy UTs
* Extract disabling screen saver to plistbuddy
- Also, skip Xcode test temporarily
* PlistBuddy idempotence
- Remove systemsetup and pmset resource calls in keep_awake
until they are made idempotent
* update test and plist name
* Implement binary plist conversion
- Changes to binary plists via PlistBuddy must be converted back
to binary since the change implicitly converts the file to xml.
This change contains logic to convert back to binary if the file is
detected to be binary in the first place.
* attempts at idempotence using chef helper methods
* early commit to get it on the remote
* implement logic for plist return type to ruby data type
* Add floating type value support
A wild FAILING TEST appeared!
...
HANKO used TDD!
It's super effective!
* move function
* real idempotence!
* passing all unit tests
* update test cookbook name
* update new_users integration tests
* systemsetup idempotence
* all smoke and unit tests passing
* quick fix for timezone bug
* remove kcpassword file as it is no longer needed
* update path for test cookbook to be default repo name
* Rename keep_awake_spec.rb to keep_awake_test.rb
* Rename preferences_spec.rb to preferences_test.rb
* Rename new_users_spec.rb to new_users_test.rb
* fix typo
* Use relative path for macos cookbook source
* Improve readability of relative path for macos cookbook source
* Bump version
* Addressing changes in Pull Request #14
- Fix bug where screensaver was not properly being disabled, update
tests accordingly
- Rename plistbuddy resource to plist, including all related files
and callers
- Rename kcpassword helper to macos_user to match helper -> resource
convention; fix top level namespace to be MacOS
- Correctly name recipe unit tests
- Implement plist resource into disable_software_updates.rb recipe
- Add disable_software_updates.rb recipe to default kitchen suite and
appropriate smoke tests
- Remove relatively useless logic in systemsetup value property coercion
* Implement binary plist conversion
* Fix unit test and apply the Law of Demeter
* update helper pattern to match current standard; include everywhere
* remove helper reference in Xcode resource
* bump version
* update Xcode attribute to latest
* extract xcode smoke tests to a different suite
* update xcversion path for simulators test
* update namespace again
* refactor XcodeHelper modules in XCVersion, Xcode, and XCVersion modules
updated methods to simplify site calls in resource
* updated method calls for new modules
* bump version
* extract xcversion module to separate library file
* add missing macos module
* include parent module macos instead of child for xcode and xcversion
* update available_simulator_versions to new method
* bump version
* add platform-specific smoke test for xcode
* add xcode test recipe
* update kitchen with xcode suite
* refactor simulator method to call it's own available list of simulators
* use new simulator method to find highest available version
* move xcode prep work into the setup action
* clear kitchen customizations
* lazily evalutate ios simulator property
* move lazy evaluation to simulator action in xcode resource
* revert lazy execution
* warn and retry if simulator list is empty
* restore spacing
* more whitespace
* refactor highest_semantic_simulator method into class, smaller methods
* implement simulator class
* add more xcversion command methods
* implement xcversion command methods
* implement command methods correctly
* add list methods for xcversion
* implement xcversion list methods
* implement install xcode correctly
* reorder methods
* add method to list installed xcodes
* use more descriptive name for 'command' method
* move where the space lives
* implement installed xcodes command
* implement install xcodes correctly
* disable downloads prior to disabling software update check
* Renamed name to device_name
* Change name.rb to machine_name.rb to be similar with the apex
automation cookbook. Instances of name are also all changed to
machine_name.
* Add new test recipe and smoke test for machine_name.
* add disable-swu test suite
* Bump up cookbook version numbers.
* update kitchen test suite order, remove standalone swu testing
* add sleep for El Capitan platform to avoid race condition
this occurs when integrated with the macos-user test recipe
* Improve README/Documentation (#23)
* implement build definition badge and minor tweaks
* Add descriptions and attribute lists for recipes
* Remove 'description' label from recipe names
* Bump version, add documentation dir
* Fix up the last commit
* Separate 10.12 and 10.13 build status
* Add table for build status
* Add plist resource documentation
* Add Xcode documentation
* bump version
This commit is contained in:
Родитель
20c24e83f5
Коммит
3ca39c8237
|
@ -68,6 +68,7 @@ kitchen-tests/vendor
|
|||
|
||||
# Visual Studio Code files
|
||||
.vscode
|
||||
*/**/*.code-workspace
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
.idea
|
||||
|
||||
|
@ -101,3 +102,4 @@ Vagrantfile
|
|||
.rubocop.yml
|
||||
.vagrant
|
||||
data_bags
|
||||
*.code-workspace
|
||||
|
|
123
README.md
123
README.md
|
@ -1,18 +1,31 @@
|
|||
# macOS Cookbook
|
||||
macOS Cookbook
|
||||
==============
|
||||
|
||||
This cookbook provides:
|
||||
- Resources for configuring and provisioning macOS.
|
||||
- Recipes that implement common use-cases of the macOS cookbook's recources.
|
||||
The macOS cookbook is a Chef library cookbook that provides resources for configuring
|
||||
and provisioning macOS. Additionally, it provides recipes that implement common
|
||||
use-cases of the macOS cookbook's recources.
|
||||
|
||||
## Platforms
|
||||
|||
|
||||
|-|-|
|
||||
| macOS Sierra 10.12 | ![build-status-1012](https://office.visualstudio.com/_apis/public/build/definitions/59d72877-1cea-4eb6-9d06-66716573631a/2143/badge) |
|
||||
| macOS High Sierra 10.13 | ![build-status-1013](https://office.visualstudio.com/_apis/public/build/definitions/59d72877-1cea-4eb6-9d06-66716573631a/2140/badge) |
|
||||
|||
|
||||
|
||||
- macOS
|
||||
Requirements
|
||||
------------
|
||||
|
||||
## Chef
|
||||
- Only tested on Chef 13
|
||||
- Surprisingly, this cookbook is only compatible with macOS
|
||||
|
||||
- Chef 13+
|
||||
Supported OS Versions
|
||||
---------------------
|
||||
|
||||
## Attributes
|
||||
- OS X El Capitan 10.11
|
||||
- macOS Sierra 10.12
|
||||
- macOS High Sierra 10.13
|
||||
|
||||
Attributes
|
||||
----------
|
||||
|
||||
### Admin User and Password
|
||||
|
||||
|
@ -22,33 +35,89 @@ node['macos']['admin_password'] = 'vagrant'
|
|||
```
|
||||
|
||||
Each of these attributes defaults to vagrant since our resources are developed
|
||||
with the Vagrant paradigm. In other words, the use and password declared here
|
||||
should be an admin user.
|
||||
with the Vagrant paradigm. In other words, the user and password declared here
|
||||
should be an admin user with passwordless super-user rights.
|
||||
|
||||
Recipes
|
||||
-------
|
||||
|
||||
### Disable Software Updates
|
||||
|
||||
Disables automatic checking and downloading of software updates.
|
||||
|
||||
**Usage:** `include_recipe macos::disable_software_updates`
|
||||
|
||||
No attributes used in this recipe.
|
||||
|
||||
### Keep Awake
|
||||
|
||||
Prevent macOS from falling asleep, disable the screensaver, and
|
||||
several other settings to always keep macOS on. Uses the `plistbuddy` and `pmset`
|
||||
resources.
|
||||
|
||||
**Usage:** `include_recipe macos::keep_awake`
|
||||
|
||||
| Attribute used | Default value |
|
||||
|---------------------------------------|-------------------------|
|
||||
| `node['macos']['network_time_server']`| `'time.windows.com'` |
|
||||
| `node['macos']['time_zone']` | `'America/Los_Angeles'` |
|
||||
|
||||
### Mono
|
||||
|
||||
_TODO_
|
||||
Installs [Mono](http://www.mono-project.com/docs/about-mono/). Requires package
|
||||
name, version number, and checksum in order to override.
|
||||
|
||||
---
|
||||
**Usage:** `include_recipe macos::mono`
|
||||
|
||||
## Resources
|
||||
| Attribute used | Default value |
|
||||
|-------------------------------------|----------------------------------------|
|
||||
| `node['macos']['mono']['package']` | `'MonoFramework-MDK-4.4.2.11.macos10.xamarin.universal.pkg'` |
|
||||
| `node['macos']['mono']['version']` | `'4.4.2'` |
|
||||
| `node['macos']['mono']['checksum']` | `'d8bfbee7ae4d0d1facaf0ddfb70c0de4b1a3d94bb1b4c38e8fa4884539f54e23'` |
|
||||
|
||||
- `ard`
|
||||
- `name`
|
||||
- `defaults`
|
||||
- `pmset`
|
||||
- `systemsetup`
|
||||
- `xcode`
|
||||
### Xcode
|
||||
|
||||
Checkout the [Wiki](https://github.com/Microsoft/macos-cookbook/wiki) for details about the macOS Cookbook resources.
|
||||
Installs Xcode 9.1 and simulators for iOS 10 and iOS 11. Check out
|
||||
the documentation for the Xcode resource if you need more flexibility.
|
||||
|
||||
---
|
||||
:large_orange_diamond: Requires an `apple_id` data bag item.
|
||||
|
||||
## Recipes
|
||||
**Usage:** `include_recipe macos::xcode`
|
||||
|
||||
- `disable_software_updates`
|
||||
- `keep_awake`
|
||||
- `mono`
|
||||
- `configurator`
|
||||
| Attribute Used | Default value |
|
||||
|---------------------------------------------------------------|---------------|
|
||||
| `node['macos']['xcode']['version']` | `'9.1'` |
|
||||
| `node['macos']['xcode']['simulator']['major_version']` | `%w(11 10)` |
|
||||
|
||||
### Apple Configurator 2
|
||||
|
||||
Installs Apple Configurator 2 using `mas` and links `cfgutil` to
|
||||
`/usr/local/bin`.
|
||||
|
||||
:large_orange_diamond: Requires an `apple_id` data bag item.
|
||||
|
||||
**Usage:** `include_recipe macos::configurator`
|
||||
|
||||
**Attributes**: No attributes used in this recipe.
|
||||
|
||||
#### Data Bags
|
||||
|
||||
Both the `macos::xcode` and `macos::configurator` recipes require a data bag
|
||||
item named `apple_id` containing valid Apple ID credentials. For example:
|
||||
|
||||
**Example:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "apple_id",
|
||||
"apple_id": "farva@spurbury.gov",
|
||||
"password": "0k@yN0cR34m"
|
||||
}
|
||||
```
|
||||
|
||||
Resources
|
||||
---------
|
||||
|
||||
- [ARD (Apple Remote Desktop)](./documentation/resource_ard.md)
|
||||
- [Plist](./documentation/resource_plist.md)
|
||||
- [Xcode](./documentation/resource_xcode.md)
|
||||
|
|
|
@ -5,5 +5,8 @@ default['macos']['mono']['package'] = 'MonoFramework-MDK-4.4.2.11.macos10.xamari
|
|||
default['macos']['mono']['version'] = '4.4.2'
|
||||
default['macos']['mono']['checksum'] = 'd8bfbee7ae4d0d1facaf0ddfb70c0de4b1a3d94bb1b4c38e8fa4884539f54e23'
|
||||
|
||||
default['macos']['xcode']['version'] = '9.0'
|
||||
default['macos']['xcode']['version'] = '9.1'
|
||||
default['macos']['xcode']['simulator']['major_version'] = %w(11 10)
|
||||
|
||||
default['macos']['network_time_server'] = 'time.windows.com'
|
||||
default['macos']['time_zone'] = 'America/Los_Angeles'
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
ard
|
||||
===
|
||||
|
||||
Use the **ard** resource to manage Remote Desktop settings and preferences.
|
||||
Under the hood, an **ard** resource executes the `kickstart` command, located
|
||||
in ARDAgent.app (one of the CoreServices of macOS). It has some basic actions,
|
||||
which pertain to the simple `kickstart` subcommands. It also has the more
|
||||
complicated `:configure` action, which requires some familiarity with
|
||||
`kickstart`.
|
||||
|
||||
[Learn more about the `kickstart` command](https://support.apple.com/en-us/HT201710).
|
||||
|
||||
Syntax
|
||||
------
|
||||
|
||||
A **ard** resource block declares a basic description of the command configuration
|
||||
and a set of properties depending on the actions executed. For example:
|
||||
|
||||
```ruby
|
||||
ard 'activate and configure ard' do
|
||||
action [:activate, :configure]
|
||||
end
|
||||
```
|
||||
|
||||
where
|
||||
|
||||
- `:activate` activates the ARD agent
|
||||
- `:configure` configures the agent using the `kickstart` defaut commandline arguments.
|
||||
|
||||
The full syntax for all of the properties that are available to the **ard**
|
||||
resource is:
|
||||
|
||||
```ruby
|
||||
ard 'description' do
|
||||
install_package String
|
||||
uninstall_options Array, # defaults to ['-files', '-settings', '-prefs'] if not specified
|
||||
restart_options Array, # defaults to ['-agent', '-console', '-menu'] if not specified
|
||||
users Array
|
||||
privs Array, # defaults to ['-all'] if not specified
|
||||
access String, # defaults to '-on' if not specified
|
||||
allow_access_for String, # defaults to '-allUsers' if not specified
|
||||
computerinfo Array
|
||||
clientopts Array
|
||||
action Symbol # defaults to [:activate, :configure] if not specified
|
||||
end
|
||||
```
|
||||
|
||||
**Note:** Not all properties are compatible with each action.
|
||||
|
||||
Actions
|
||||
-------
|
||||
|
||||
This resource has the following actions:
|
||||
|
||||
`:activate`
|
||||
|
||||
Activate the remote desktop agent.
|
||||
|
||||
`:deactivate`
|
||||
|
||||
Deactivate the remote desktop agent.
|
||||
|
||||
`:uninstall`
|
||||
|
||||
Uninstall a package from another remotely
|
||||
managed mac.
|
||||
|
||||
`:stop`
|
||||
|
||||
Stop the agent.
|
||||
|
||||
`:restart`
|
||||
|
||||
Restart the remote desktop agent.
|
||||
|
||||
`:configure`
|
||||
|
||||
Configure the setup of the remote desktop
|
||||
agent using the default options. If you were to configure the default options,
|
||||
your settings would look like this in the GUI:
|
||||
|
||||
Properties
|
||||
----------
|
||||
|
||||
`install_package`
|
||||
|
||||
**Ruby Type:** `String`
|
||||
|
||||
`uninstall_options`
|
||||
|
||||
**Ruby type:** `Array`
|
||||
|
||||
default options: `['-files', '-settings', '-prefs']`
|
||||
|
||||
`restart_options`
|
||||
|
||||
**Ruby type:** `Array`
|
||||
|
||||
default options: `['-agent', '-console', '-menu']`
|
||||
|
||||
`users`
|
||||
|
||||
**Ruby type:** `Array`
|
||||
|
||||
`privs`
|
||||
|
||||
**Ruby type:** `Array`
|
||||
|
||||
default: `['-all']`
|
||||
|
||||
`access`
|
||||
|
||||
**Ruby type:** `String`
|
||||
|
||||
default: `'-on'`
|
||||
|
||||
`allow_access_for`
|
||||
|
||||
**Ruby type:** `String`
|
||||
|
||||
default: `'-allUsers'`
|
||||
|
||||
`computerinfo`
|
||||
|
||||
**Ruby type:** `Array`
|
||||
|
||||
`clientopts`
|
||||
|
||||
**Ruby type:** `Array`
|
||||
|
||||
`action`
|
||||
|
||||
**Ruby type:** `Symbol`
|
|
@ -0,0 +1,64 @@
|
|||
plist
|
||||
=====
|
||||
|
||||
Use the **plist** resource to manage property list files (plists) and their content.
|
||||
A **plist** resource instance represents the state of a single key-value pair in
|
||||
the delared plist `path`. Since each plist resource instance represents only one
|
||||
setting, you may end up with several plist resource calls in a given recipe. Although
|
||||
this may seem like overkill, it allows us to have a fully idempotent resource with
|
||||
fine granularity.
|
||||
|
||||
During the `chef-client` run, the client knows to check the state of the plist
|
||||
before changing any values. It also makes sure that the plist is in binary format
|
||||
so that the settings can be interpreted correctly by the operating system.
|
||||
|
||||
Prior knowledge of using commandline utilities such as `/usr/bin/defaults`
|
||||
and `/usr/libexec/PlistBuddy` will be useful when implementing the
|
||||
**plist** resource.
|
||||
|
||||
[Learn more about property lists.](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/PropertyLists/QuickStartPlist/QuickStartPlist.html#//apple_ref/doc/uid/10000048i-CH4-SW5)
|
||||
|
||||
Syntax
|
||||
------
|
||||
|
||||
The full syntax for all of the properties that are available to the **plist**
|
||||
resource is:
|
||||
|
||||
```ruby
|
||||
plist 'description' do
|
||||
path String # defaults to 'description' if not specified
|
||||
entry String
|
||||
value TrueClass, FalseClass, String, Integer, Float
|
||||
action Symbol # defaults to :set if not specified
|
||||
end
|
||||
```
|
||||
|
||||
Actions
|
||||
-------
|
||||
|
||||
This resource has the following actions:
|
||||
|
||||
`:set`
|
||||
|
||||
Set `entry` to `value` in `path`
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
Enabling the setting to show both visible and invisible files.
|
||||
|
||||
```ruby
|
||||
plist 'show hidden files' do
|
||||
path '/Users/vagrant/Library/Preferences/com.apple.finder.plist'
|
||||
entry 'AppleShowAllFiles'
|
||||
value true
|
||||
end
|
||||
```
|
||||
|
||||
where
|
||||
|
||||
`path` is the absolute path to the `com.apple.finder.plist` plist file
|
||||
|
||||
`entry` is the representing the plist entry `'AppleShowAllFiles'`
|
||||
|
||||
`value` is the entry's value to boolean type: `true`
|
|
@ -0,0 +1,69 @@
|
|||
xcode
|
||||
=====
|
||||
|
||||
Use the **xcode** resource to manage a single installation of Apple's Xcode IDE.
|
||||
An **xcode** resource instance represents the state of a single Xcode installation
|
||||
and any simulators that are declared using the `ios_simulators` property. The latest
|
||||
version of iOS simulators are always installed with Xcode.
|
||||
|
||||
Syntax
|
||||
------
|
||||
|
||||
The full syntax for all of the properties that are available to the **xcode**
|
||||
resource is:
|
||||
|
||||
```ruby
|
||||
xcode 'description' do
|
||||
version String # defaults to 'description' if not specified
|
||||
path String # defaults to '/Applications/Xcode.app' if not specified
|
||||
ios_simulators Array # defaults to [10, 11] if not specified
|
||||
action Symbol # defaults to [:install_xcode, :install_simulators] if not specified
|
||||
end
|
||||
```
|
||||
|
||||
Actions
|
||||
-------
|
||||
|
||||
This resource has the following actions:
|
||||
|
||||
`:install_xcode`
|
||||
|
||||
Set `entry` to `value` in `path`
|
||||
|
||||
`:install_simulators`
|
||||
|
||||
Along with a `version` of Xcode,
|
||||
install the declared array of the major versions of `ios_simulators`.
|
||||
|
||||
Examples
|
||||
--------
|
||||
|
||||
### Basic usage
|
||||
|
||||
The **xcode** resource in its simplest form:
|
||||
|
||||
```ruby
|
||||
xcode '9.2'
|
||||
```
|
||||
|
||||
### Using with node attributes
|
||||
|
||||
Install different versions of Xcode based on the macOS version:
|
||||
|
||||
```ruby
|
||||
if node['platform_version'].match?(/10\.13/) || node['platform_version'].match?(/10\.12/)
|
||||
execute 'Disable Gatekeeper' do
|
||||
command 'spctl --master-disable'
|
||||
end
|
||||
|
||||
xcode '9.2' do
|
||||
ios_simulators %w(11 10)
|
||||
end
|
||||
|
||||
elsif node['platform_version'].match?(/10\.11/)
|
||||
|
||||
xcode '8.2.1' do
|
||||
ios_simulators %w(10 9)
|
||||
end
|
||||
end
|
||||
```
|
|
@ -0,0 +1,36 @@
|
|||
module MacOS
|
||||
module MacOSUserHelpers
|
||||
def kcpassword_hash(password)
|
||||
bits = magic_bits
|
||||
obfuscated = []
|
||||
padded(password).each do |char|
|
||||
obfuscated.push(bits[0] ^ char)
|
||||
bits.rotate!
|
||||
end
|
||||
obfuscated.pack('C*')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def magic_bits
|
||||
[125, 137, 82, 35, 210, 188, 221, 234, 163, 185, 31]
|
||||
end
|
||||
|
||||
def magic_len
|
||||
magic_bits.length
|
||||
end
|
||||
|
||||
def padded(password)
|
||||
padding = magic_len - (password.length % magic_len)
|
||||
padding_size = padding > 0 ? padding : 0
|
||||
translated(password) + ([0] * padding_size)
|
||||
end
|
||||
|
||||
def translated(password)
|
||||
password.split('').map(&:ord)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Chef::Recipe.include(MacOS::MacOSUserHelpers)
|
||||
Chef::Resource.include(MacOS::MacOSUserHelpers)
|
|
@ -0,0 +1,90 @@
|
|||
module MacOS
|
||||
module PlistHelpers
|
||||
def convert_to_data_type_from_string(type, value)
|
||||
case type
|
||||
when 'boolean'
|
||||
value.to_i == 1
|
||||
when 'integer'
|
||||
value.to_i
|
||||
when 'float'
|
||||
value.to_f
|
||||
when 'string'
|
||||
value
|
||||
else
|
||||
raise "Unknown or unsupported data type: #{type.class}"
|
||||
end
|
||||
end
|
||||
|
||||
def convert_to_string_from_data_type(value)
|
||||
data_type_cases = { Array => "array #{value}",
|
||||
Integer => "integer #{value}",
|
||||
TrueClass => "bool #{value}",
|
||||
FalseClass => "bool #{value}",
|
||||
Hash => "dict #{value}",
|
||||
String => "string #{value}",
|
||||
Float => "float #{value}" }
|
||||
data_type_cases[value.class]
|
||||
end
|
||||
|
||||
def type_to_commandline_string(value)
|
||||
case value
|
||||
when Array
|
||||
'array'
|
||||
when Integer
|
||||
'integer'
|
||||
when FalseClass
|
||||
'bool'
|
||||
when TrueClass
|
||||
'bool'
|
||||
when Hash
|
||||
'dict'
|
||||
when String
|
||||
'string'
|
||||
when Float
|
||||
'float'
|
||||
else
|
||||
raise "Unknown or unsupported data type: #{value} of #{value.class}"
|
||||
end
|
||||
end
|
||||
|
||||
def hardware_uuid
|
||||
system_profiler_hardware_output = shell_out('system_profiler', 'SPHardwareDataType').stdout
|
||||
hardware_overview = Psych.load(system_profiler_hardware_output)['Hardware']['Hardware Overview']
|
||||
hardware_overview['Hardware UUID']
|
||||
end
|
||||
|
||||
def plistbuddy_command(subcommand, entry, path, value = nil)
|
||||
arg = case subcommand.to_s
|
||||
when 'add'
|
||||
type_to_commandline_string(value)
|
||||
when 'print'
|
||||
''
|
||||
else
|
||||
value
|
||||
end
|
||||
entry = "\"#{entry}\"" if entry.include?(' ')
|
||||
entry_with_arg = [entry, arg].join(' ').strip
|
||||
subcommand = "#{subcommand.capitalize} :#{entry_with_arg}"
|
||||
[plistbuddy_executable, '-c', "\'#{subcommand}\'", path].join(' ')
|
||||
end
|
||||
|
||||
def system_preference(path, entry)
|
||||
defaults_read_type_output = shell_out(defaults_executable, 'read-type', path, entry).stdout
|
||||
defaults_read_output = shell_out(defaults_executable, 'read', path, entry).stdout
|
||||
{ key_type: defaults_read_type_output.split.last, key_value: defaults_read_output.strip }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def defaults_executable
|
||||
'/usr/bin/defaults'
|
||||
end
|
||||
|
||||
def plistbuddy_executable
|
||||
'/usr/libexec/PlistBuddy'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Chef::Recipe.include(MacOS::PlistHelpers)
|
||||
Chef::Resource.include(MacOS::PlistHelpers)
|
|
@ -1,56 +1,64 @@
|
|||
module Xcode
|
||||
module Helper
|
||||
def xcversion_command
|
||||
'/opt/chef/embedded/bin/xcversion'.freeze
|
||||
end
|
||||
include Chef::Mixin::ShellOut
|
||||
|
||||
def xcode_already_installed?(semantic_version)
|
||||
xcversion_output = shell_out("#{xcversion_command} installed").stdout.split
|
||||
installed_xcodes = xcversion_output.values_at(*xcversion_output.each_index.select(&:even?))
|
||||
installed_xcodes.include?(semantic_version)
|
||||
end
|
||||
module MacOS
|
||||
module Xcode
|
||||
class << self
|
||||
def installed?(semantic_version)
|
||||
xcversion_output = shell_out(XCVersion.installed_xcodes).stdout.split
|
||||
installed_xcodes = xcversion_output.values_at(*xcversion_output.each_index.select(&:even?))
|
||||
installed_xcodes.include?(semantic_version)
|
||||
end
|
||||
|
||||
def xcversion_version(semantic_version)
|
||||
split_version = semantic_version.split('.')
|
||||
if split_version.length == 2 && split_version.last == '0'
|
||||
split_version.first
|
||||
else
|
||||
semantic_version
|
||||
def bundle_version_correct?
|
||||
xcode_version = '/Applications/Xcode.app/Contents/version.plist CFBundleShortVersionString'
|
||||
node['macos']['xcode']['version'] == shell_out("defaults read #{xcode_version}").stdout.strip
|
||||
end
|
||||
end
|
||||
|
||||
def requested_xcode_not_at_path
|
||||
xcode_version = '/Applications/Xcode.app/Contents/version.plist CFBundleShortVersionString'
|
||||
node['macos']['xcode']['version'] != shell_out("defaults read #{xcode_version}").stdout.strip
|
||||
end
|
||||
class Simulator
|
||||
attr_reader :version
|
||||
|
||||
def simulator_already_installed?(semantic_version)
|
||||
available_simulator_versions.include?("#{semantic_version} Simulator (installed)")
|
||||
end
|
||||
|
||||
def highest_semantic_simulator_version(major_version, simulators)
|
||||
requirement = Gem::Dependency.new('iOS', "~> #{major_version}")
|
||||
highest = simulators.select { |name, vers| requirement.match?(name, vers) }.max
|
||||
if highest.nil?
|
||||
Chef::Application.fatal!("iOS #{major_version} Simulator no longer available from Apple!")
|
||||
else
|
||||
highest.join(' ')
|
||||
def initialize(major_version)
|
||||
while available_list.empty?
|
||||
Chef::Log.warn('iOS Simulator list not populated yet')
|
||||
sleep 10
|
||||
end
|
||||
if latest_semantic_version(major_version).nil?
|
||||
Chef::Application.fatal!("iOS #{major_version} Simulator no longer available from Apple!")
|
||||
else
|
||||
@version = latest_semantic_version(major_version).join(' ')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def included_simulator_major_version
|
||||
version_matcher = /\d{1,2}\.\d{0,2}\.?\d{0,3}/
|
||||
sdks = shell_out!('/usr/bin/xcodebuild -showsdks').stdout
|
||||
included_simulator = sdks.match(/Simulator - iOS (?<version>#{version_matcher})/)
|
||||
included_simulator[:version].split('.').first.to_i
|
||||
end
|
||||
def latest_semantic_version(major_version)
|
||||
requirement = Gem::Dependency.new('iOS', "~> #{major_version}")
|
||||
available_list.select { |platform, version| requirement.match?(platform, version) }.max
|
||||
end
|
||||
|
||||
def simulator_list
|
||||
available_simulator_versions.split(/\n/).map { |version| version.split[0...2] }
|
||||
end
|
||||
def available_list
|
||||
available_versions.split(/\n/).map { |version| version.split[0..1] }
|
||||
end
|
||||
|
||||
def available_simulator_versions
|
||||
shell_out!("#{xcversion_command} simulators").stdout
|
||||
def available_versions
|
||||
shell_out!(XCVersion.list_simulators).stdout
|
||||
end
|
||||
|
||||
class << self
|
||||
def installed?(semantic_version)
|
||||
shell_out!(XCVersion.list_simulators)
|
||||
.stdout.include?("#{semantic_version} Simulator (installed)")
|
||||
end
|
||||
|
||||
def included_major_version
|
||||
version_matcher = /\d{1,2}\.\d{0,2}\.?\d{0,3}/
|
||||
sdks = shell_out!('/usr/bin/xcodebuild -showsdks').stdout
|
||||
included_simulator = sdks.match(/Simulator - iOS (?<version>#{version_matcher})/)
|
||||
included_simulator[:version].split('.').first.to_i
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Chef::Recipe.include(MacOS)
|
||||
Chef::Resource.include(MacOS)
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
module MacOS
|
||||
module XCVersion
|
||||
class << self
|
||||
def xcversion
|
||||
'/opt/chef/embedded/bin/xcversion '.freeze
|
||||
end
|
||||
|
||||
def update
|
||||
xcversion + 'update'
|
||||
end
|
||||
|
||||
def list_simulators
|
||||
xcversion + 'simulators'
|
||||
end
|
||||
|
||||
def install_simulator(version)
|
||||
xcversion + "simulators --install='#{version}'"
|
||||
end
|
||||
|
||||
def list_xcodes
|
||||
xcversion + 'list'
|
||||
end
|
||||
|
||||
def install_xcode(version)
|
||||
xcversion + "install '#{apple_pseudosemantic_version(version)}'"
|
||||
end
|
||||
|
||||
def installed_xcodes
|
||||
xcversion + 'installed'
|
||||
end
|
||||
|
||||
def apple_pseudosemantic_version(semantic_version)
|
||||
split_version = semantic_version.split('.')
|
||||
if split_version.length == 2 && split_version.last == '0'
|
||||
split_version.first
|
||||
else
|
||||
semantic_version
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Chef::Recipe.include(MacOS)
|
||||
Chef::Resource.include(MacOS)
|
|
@ -5,7 +5,7 @@ license 'MIT'
|
|||
description 'Resources for configuring and provisioning macOS'
|
||||
long_description 'Resources for configuring and provisioning macOS'
|
||||
chef_version '~> 13.0' if respond_to?(:chef_version)
|
||||
version '0.9.0'
|
||||
version '1.0.0'
|
||||
|
||||
source_url 'https://github.com/Microsoft/macos-cookbook'
|
||||
issues_url 'https://github.com/Microsoft/macos-cookbook/issues'
|
||||
|
@ -13,3 +13,5 @@ issues_url 'https://github.com/Microsoft/macos-cookbook/issues'
|
|||
supports 'mac_os_x'
|
||||
|
||||
depends 'homebrew'
|
||||
|
||||
gem 'colorize'
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
defaults '/Library/Preferences/com.apple.SoftwareUpdate' do
|
||||
settings 'AutomaticCheckEnabled' => false,
|
||||
'AutomaticDownload' => false
|
||||
plist 'disable automatic software update downloads' do
|
||||
path '/Library/Preferences/com.apple.SoftwareUpdate.plist'
|
||||
entry 'AutomaticDownload'
|
||||
value false
|
||||
end
|
||||
|
||||
sleep 10 if node['platform_version'].match?(/10\.11/)
|
||||
|
||||
plist 'disable automatic software update check' do
|
||||
path '/Library/Preferences/com.apple.SoftwareUpdate.plist'
|
||||
entry 'AutomaticCheckEnabled'
|
||||
value false
|
||||
end
|
||||
|
|
|
@ -1,41 +1,80 @@
|
|||
systemsetup 'keep awake and set time with systemsetup' do
|
||||
set sleep: 0,
|
||||
computersleep: 0,
|
||||
displaysleep: 0,
|
||||
harddisksleep: 0,
|
||||
remoteappleevents: 'On',
|
||||
allowpowerbuttontosleepcomputer: 'Off',
|
||||
waitforstartupafterpowerfailure: 0,
|
||||
restartfreeze: 'On',
|
||||
networktimeserver: 'time.windows.com',
|
||||
timezone: 'America/Los_Angeles'
|
||||
plist 'disable Power Nap' do
|
||||
path '/Library/Preferences/com.apple.PowerManagement.plist'
|
||||
entry 'DarkWakeBackgroundTasks'
|
||||
value false
|
||||
end
|
||||
|
||||
pmset 'keep awake with /usr/bin/pmset' do
|
||||
settings sleep: 0,
|
||||
hibernatemode: 0,
|
||||
womp: 1, # wake on ethernet magic packet
|
||||
hibernatefile: '/var/vm/sleepimage',
|
||||
ttyskeepawake: 1
|
||||
plist 'enable automatic restart on power loss' do
|
||||
path '/Library/Preferences/com.apple.PowerManagement.plist'
|
||||
entry 'Automatic Restart On Power Loss'
|
||||
value true
|
||||
end
|
||||
|
||||
defaults '/Library/Preferences/com.apple.PowerManagement' do
|
||||
settings 'DarkWakeBackgroundTasks' => false, # power nap
|
||||
'"Automatic Restart On Power Loss"' => true,
|
||||
'"Display Sleep Timer"' => 0,
|
||||
'"Disk Sleep Timer"' => 0,
|
||||
'"Wake On LAN"' => true,
|
||||
'"System Sleep Timer"' => 0
|
||||
plist 'set display sleep timer to zero' do
|
||||
path '/Library/Preferences/com.apple.PowerManagement.plist'
|
||||
entry 'Display Sleep Timer'
|
||||
value 0
|
||||
end
|
||||
|
||||
ruby_block 'set power settings to autoLoginUser' do
|
||||
block do
|
||||
loginwindow_plist = '/Library/Preferences/com.apple.loginwindow'
|
||||
auto_login_user = "defaults read #{loginwindow_plist} autoLoginUser"
|
||||
node.default['macos']['power']['owner'] = shell_out!(auto_login_user).stdout.strip
|
||||
end
|
||||
plist 'enable wake from sleep via network' do
|
||||
path '/Library/Preferences/com.apple.PowerManagement.plist'
|
||||
entry 'Wake On LAN'
|
||||
value true
|
||||
end
|
||||
|
||||
execute 'disable screensaver' do
|
||||
command lazy { "sudo -u #{node['macos']['power']['owner']} defaults -currentHost write com.apple.screensaver idleTime 0" }
|
||||
plist 'set system sleep timer to zero' do
|
||||
path '/Library/Preferences/com.apple.PowerManagement.plist'
|
||||
entry 'System Sleep Timer'
|
||||
value 0
|
||||
end
|
||||
|
||||
plist 'disable screensaver' do
|
||||
path "/Users/#{node['macos']['admin_user']}/Library/Preferences/ByHost/com.apple.screensaver.#{hardware_uuid}.plist"
|
||||
entry 'idleTime'
|
||||
value 0
|
||||
end
|
||||
|
||||
systemsetup 'Set amount of idle time until computer sleeps to never' do
|
||||
setting 'computersleep'
|
||||
value 'Never'
|
||||
end
|
||||
|
||||
systemsetup 'set amount of idle time until display sleeps to never' do
|
||||
setting 'displaysleep'
|
||||
value 'Never'
|
||||
end
|
||||
|
||||
systemsetup 'set amount of idle time until hard disk sleeps to never' do
|
||||
setting 'harddisksleep'
|
||||
value 'Never'
|
||||
end
|
||||
|
||||
systemsetup 'set remote apple events to on' do
|
||||
setting 'remoteappleevents'
|
||||
value 'On'
|
||||
end
|
||||
|
||||
systemsetup 'disable the power button from being able to sleep the computer.' do
|
||||
setting 'allowpowerbuttontosleepcomputer'
|
||||
value 'Off'
|
||||
end
|
||||
|
||||
systemsetup 'set the number of seconds after which the computer will start up after a power failure to zero.' do
|
||||
setting 'waitforstartupafterpowerfailure'
|
||||
value 0
|
||||
end
|
||||
|
||||
systemsetup 'set restart on freeze to on' do
|
||||
setting 'restartfreeze'
|
||||
value 'On'
|
||||
end
|
||||
|
||||
systemsetup "set network time server to #{node['macos']['network_time_server']}" do
|
||||
setting 'networktimeserver'
|
||||
value node['macos']['network_time_server']
|
||||
end
|
||||
|
||||
systemsetup "set current time zone to #{node['macos']['time_zone']}" do
|
||||
setting 'timezone'
|
||||
value node['macos']['time_zone']
|
||||
end
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
resource_name :name
|
||||
resource_name :machine_name
|
||||
default_action :run
|
||||
|
||||
BASE_COMMAND = '/usr/sbin/scutil'.freeze
|
||||
SMB_SERVER_PLIST = '/Library/Preferences/SystemConfiguration/com.apple.smb.server'.freeze
|
||||
|
||||
property :name, String, name_property: true
|
||||
property :machine_name, String, name_property: true
|
||||
|
||||
# We cannot set the LocalHostName here because it does not conform to
|
||||
# the DNS standards outlined in RFC 1034 (section 3.5)
|
||||
|
||||
action :run do
|
||||
execute BASE_COMMAND do
|
||||
command "#{BASE_COMMAND} --set HostName '#{new_resource.name}'"
|
||||
command "#{BASE_COMMAND} --set HostName '#{new_resource.machine_name}'"
|
||||
end
|
||||
|
||||
execute BASE_COMMAND do
|
||||
command "#{BASE_COMMAND} --set ComputerName '#{new_resource.name}'"
|
||||
command "#{BASE_COMMAND} --set ComputerName '#{new_resource.machine_name}'"
|
||||
end
|
||||
|
||||
defaults SMB_SERVER_PLIST do
|
||||
settings 'NetBIOSName' => new_resource.name
|
||||
settings 'NetBIOSName' => new_resource.machine_name
|
||||
end
|
||||
end
|
|
@ -0,0 +1,78 @@
|
|||
resource_name :macos_user
|
||||
default_action :create
|
||||
|
||||
property :username, String, name_property: true
|
||||
property :password, String, default: 'password'
|
||||
property :autologin, [TrueClass]
|
||||
property :admin, [TrueClass]
|
||||
|
||||
action_class do
|
||||
def user_home
|
||||
::File.join('/Users', new_resource.username)
|
||||
end
|
||||
|
||||
def setup_assistant_plist
|
||||
::File.join(user_home, '/Library/Preferences/com.apple.SetupAssistant.plist')
|
||||
end
|
||||
|
||||
def setup_assistant_keypair_values
|
||||
{ 'DidSeeSiriSetup' => true,
|
||||
'DidSeeCloudSetup' => true,
|
||||
'LastSeenCloudProductVersion' => node['platform_version'] }
|
||||
end
|
||||
|
||||
def sysadminctl
|
||||
'/usr/sbin/sysadminctl'
|
||||
end
|
||||
|
||||
def admin_user?
|
||||
if property_is_set?(:admin)
|
||||
'-admin'
|
||||
else
|
||||
''
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
action :create do
|
||||
execute "add user #{new_resource.username}" do
|
||||
command "#{sysadminctl} -addUser #{new_resource.username} -password #{new_resource.password} #{admin_user?}"
|
||||
not_if { ::File.exist? user_home }
|
||||
end
|
||||
|
||||
if property_is_set?(:autologin)
|
||||
setup_assistant_keypair_values.each do |key, setting|
|
||||
plist "set #{setting} to #{key}" do
|
||||
path setup_assistant_plist
|
||||
entry key
|
||||
value setting
|
||||
end
|
||||
end
|
||||
|
||||
plist "set user \"#{new_resource.username}\" to login automatically" do
|
||||
path '/Library/Preferences/com.apple.loginwindow.plist'
|
||||
entry 'autoLoginUser'
|
||||
value new_resource.username
|
||||
end
|
||||
|
||||
file '/etc/kcpassword' do
|
||||
content kcpassword_hash(new_resource.password)
|
||||
owner 'root'
|
||||
group 'wheel'
|
||||
mode '0600'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
action :delete do
|
||||
directory user_home do
|
||||
recursive true
|
||||
action :delete
|
||||
only_if { ::File.exist? user_home }
|
||||
end
|
||||
|
||||
execute "delete user: #{user}" do
|
||||
command "#{sysadminctl} -deleteUser #{new_resource.username}"
|
||||
only_if { "/usr/bin/dscl . -list /users | grep ^#{new_resource.username}$" }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,39 @@
|
|||
resource_name :plist
|
||||
|
||||
property :path, String, name_property: true
|
||||
property :entry, String, desired_state: true
|
||||
property :value, [TrueClass, FalseClass, String, Integer, Float], desired_state: true
|
||||
|
||||
action_class do
|
||||
def binary?
|
||||
file_type_output = shell_out('/usr/bin/file', '--brief', '--mime-encoding', new_resource.path).stdout
|
||||
file_type_output.strip == 'binary'
|
||||
end
|
||||
end
|
||||
|
||||
load_current_value do |desired|
|
||||
system_preference = system_preference(desired.path, desired.entry)
|
||||
current_value_does_not_exist! if system_preference[:key_type].nil?
|
||||
entry desired.entry unless system_preference[:key_type].nil?
|
||||
value convert_to_data_type_from_string(system_preference[:key_type], system_preference[:key_value])
|
||||
end
|
||||
|
||||
action :set do
|
||||
converge_if_changed :entry do
|
||||
converge_by "add \"#{new_resource.entry}\" to #{new_resource.path.split('/').last}" do
|
||||
execute plistbuddy_command(:add, new_resource.entry, new_resource.path, new_resource.value)
|
||||
end
|
||||
end
|
||||
|
||||
converge_if_changed :value do
|
||||
converge_by "set \"#{new_resource.entry}\" to #{new_resource.value} at #{new_resource.path.split('/').last}" do
|
||||
execute plistbuddy_command(:set, new_resource.entry, new_resource.path, new_resource.value)
|
||||
end
|
||||
end
|
||||
|
||||
unless binary?
|
||||
converge_by "convert \"#{new_resource.path.split('/').last}\" to binary" do
|
||||
execute "/usr/bin/plutil -convert binary1 #{new_resource.path}"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,26 +1,32 @@
|
|||
require 'colorize'
|
||||
|
||||
resource_name :systemsetup
|
||||
default_action :run
|
||||
|
||||
BASE_COMMAND = '/usr/sbin/systemsetup'.freeze
|
||||
property :setting, desired_state: false
|
||||
property :value, desired_state: true, coerce: proc { |m| m.to_s }
|
||||
|
||||
property :get, Array
|
||||
property :set, Hash
|
||||
default_action :set
|
||||
|
||||
action :run do
|
||||
if new_resource.get
|
||||
new_resource.get.each do |flag|
|
||||
execute BASE_COMMAND do
|
||||
command "#{BASE_COMMAND} -get#{flag}"
|
||||
live_stream true
|
||||
end
|
||||
end
|
||||
load_current_value do |desired|
|
||||
command_output = shell_out('/usr/sbin/systemsetup', "-get#{desired.setting}").stdout.split(': ').last.strip
|
||||
if command_output.include?('after')
|
||||
value command_output.split[1]
|
||||
elsif command_output.end_with?('seconds')
|
||||
value command_output.split.first
|
||||
elsif command_output.start_with?('Error')
|
||||
puts "\n"
|
||||
warn(command_output.to_s.colorize(:red).bold)
|
||||
warn(desired.setting.to_s.colorize(:red))
|
||||
value desired.value
|
||||
else
|
||||
value command_output.split.last
|
||||
end
|
||||
end
|
||||
|
||||
if new_resource.set
|
||||
new_resource.set.each do |flag, setting|
|
||||
execute BASE_COMMAND do
|
||||
command "#{BASE_COMMAND} -set#{flag} #{setting}"
|
||||
end
|
||||
action :set do
|
||||
converge_if_changed do
|
||||
converge_by "set #{new_resource.setting} to #{new_resource.value}" do
|
||||
execute "/usr/sbin/systemsetup -set#{new_resource.setting} #{new_resource.value}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
include Xcode::Helper
|
||||
|
||||
resource_name :xcode
|
||||
default_action %i(setup install_xcode install_simulators)
|
||||
|
||||
|
@ -11,9 +9,7 @@ action :setup do
|
|||
chef_gem 'xcode-install' do
|
||||
options('--no-document')
|
||||
end
|
||||
end
|
||||
|
||||
action :install_xcode do
|
||||
CREDENTIALS_DATA_BAG = data_bag_item(:credentials, :apple_id)
|
||||
|
||||
DEVELOPER_CREDENTIALS = {
|
||||
|
@ -23,26 +19,28 @@ action :install_xcode do
|
|||
|
||||
execute 'update available Xcode versions' do
|
||||
environment DEVELOPER_CREDENTIALS
|
||||
command "#{xcversion_command} update"
|
||||
command XCVersion.update
|
||||
end
|
||||
end
|
||||
|
||||
action :install_xcode do
|
||||
execute "install Xcode #{new_resource.version}" do
|
||||
environment DEVELOPER_CREDENTIALS
|
||||
command "#{xcversion_command} install '#{xcversion_version(new_resource.version)}'"
|
||||
not_if { xcode_already_installed?(new_resource.version) }
|
||||
command XCVersion.install_xcode(new_resource.version)
|
||||
not_if { Xcode.installed?(new_resource.version) }
|
||||
end
|
||||
end
|
||||
|
||||
action :install_simulators do
|
||||
if new_resource.ios_simulators
|
||||
new_resource.ios_simulators.each do |major_version|
|
||||
next if major_version.to_i >= included_simulator_major_version
|
||||
version = highest_semantic_simulator_version(major_version, simulator_list)
|
||||
next if major_version.to_i >= Xcode::Simulator.included_major_version
|
||||
version = Xcode::Simulator.new(major_version).version
|
||||
|
||||
execute "install latest iOS #{major_version} Simulator" do
|
||||
environment DEVELOPER_CREDENTIALS
|
||||
command "#{xcversion_command} simulators --install='#{version}'"
|
||||
not_if { simulator_already_installed?(version) }
|
||||
command XCVersion.install_simulator(version)
|
||||
not_if { Xcode::Simulator.installed?(version) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,2 +1,11 @@
|
|||
require 'chefspec'
|
||||
require 'chefspec/berkshelf'
|
||||
|
||||
require_relative '../libraries/macos_user'
|
||||
require_relative '../libraries/plist'
|
||||
require_relative '../libraries/xcode'
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.platform = 'mac_os_x'
|
||||
config.version = '10.12'
|
||||
end
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
require 'spec_helper'
|
||||
|
||||
include MacOS::MacOSUserHelpers
|
||||
|
||||
describe MacOS::MacOSUserHelpers, '#kcpassword_hash' do
|
||||
context 'When calling the obfuscate method on a short password' do
|
||||
it 'the password is obfuscated correctly' do
|
||||
expect(kcpassword_hash('password')).to eq "\r\xE8!P\xA5\xD3\xAF\x8E\xA3\xB9\x1F".force_encoding('ASCII-8BIT')
|
||||
end
|
||||
end
|
||||
|
||||
context 'When calling the obfuscate method on a long password' do
|
||||
xit 'the password hash value matches the binary content of the file' do
|
||||
expect(kcpassword_hash('correct-horse-battery-staple')).to eq "\x1E\xE6 Q\xB7\xDF\xA9\xC7\xCB\xD6m\x0E\xEC\x7FA\xB3\xC8\xA9\x8F\xD1\xC02\x0E\xFD3S\xBE\xD9\xAE\x9F\xC7\xD6\x1F".force_encoding('ASCII-8BIT')
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,139 @@
|
|||
require 'spec_helper'
|
||||
|
||||
include MacOS::PlistHelpers
|
||||
|
||||
describe MacOS::PlistHelpers, '#plist_command' do
|
||||
context 'Adding a value to a plist' do
|
||||
it 'the bool arguments contain the data type' do
|
||||
expect(plistbuddy_command(:add, 'FooEntry', 'path/to/file.plist', true)).to eq "/usr/libexec/PlistBuddy -c 'Add :FooEntry bool' path/to/file.plist"
|
||||
end
|
||||
|
||||
it 'the add command only adds the data type' do
|
||||
expect(plistbuddy_command(:add, 'QuuxEntry', 'path/to/file.plist', 50)).to eq "/usr/libexec/PlistBuddy -c 'Add :QuuxEntry integer' path/to/file.plist"
|
||||
end
|
||||
|
||||
it 'the delete command is formatted properly' do
|
||||
expect(plistbuddy_command(:delete, 'BarEntry', 'path/to/file.plist')).to eq "/usr/libexec/PlistBuddy -c 'Delete :BarEntry' path/to/file.plist"
|
||||
end
|
||||
|
||||
it 'the set command is formatted properly' do
|
||||
expect(plistbuddy_command(:set, 'BazEntry', 'path/to/file.plist', false)).to eq "/usr/libexec/PlistBuddy -c 'Set :BazEntry false' path/to/file.plist"
|
||||
end
|
||||
|
||||
it 'the print command is formatted properly' do
|
||||
expect(plistbuddy_command(:print, 'QuxEntry', 'path/to/file.plist')).to eq "/usr/libexec/PlistBuddy -c 'Print :QuxEntry' path/to/file.plist"
|
||||
end
|
||||
end
|
||||
|
||||
context 'The value provided contains spaces' do
|
||||
it 'returns the value properly formatted with double quotes' do
|
||||
expect(plistbuddy_command(:print, 'Foo Bar Baz', 'path/to/file.plist')).to eq "/usr/libexec/PlistBuddy -c 'Print :\"Foo Bar Baz\"' path/to/file.plist"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe MacOS::PlistHelpers, '#convert_to_data_type_from_string' do
|
||||
context 'When the type is boolean and given a 1 or 0' do
|
||||
it 'returns true if entry is 1' do
|
||||
expect(convert_to_data_type_from_string('boolean', '1')).to eq true
|
||||
end
|
||||
|
||||
it 'returns false if entry is 0' do
|
||||
expect(convert_to_data_type_from_string('boolean', '0')).to eq false
|
||||
end
|
||||
end
|
||||
|
||||
context 'When the type is integer and the value is 1' do
|
||||
it 'returns the value as an integer' do
|
||||
expect(convert_to_data_type_from_string('integer', '1')).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
context 'When the type is integer and the value is 0' do
|
||||
it 'returns the value as an integer' do
|
||||
expect(convert_to_data_type_from_string('integer', '0')).to eq 0
|
||||
end
|
||||
end
|
||||
|
||||
context 'When the type is integer and the value is 950224' do
|
||||
it 'returns the correct value as an integer' do
|
||||
expect(convert_to_data_type_from_string('integer', '950224')).to eq 950224
|
||||
end
|
||||
end
|
||||
|
||||
context 'When the type is string and the value is also a string' do
|
||||
it 'returns the correct value still as a string' do
|
||||
expect(convert_to_data_type_from_string('string', 'corge')).to eq 'corge'
|
||||
end
|
||||
end
|
||||
|
||||
context 'When the type is float and the value is 3.14159265359' do
|
||||
it 'returns the correct value as a float' do
|
||||
expect(convert_to_data_type_from_string('float', '3.14159265359')).to eq 3.14159265359
|
||||
end
|
||||
end
|
||||
|
||||
context 'When the type nor the value is given' do
|
||||
it 'find the value from somewhere else' do
|
||||
expect { convert_to_data_type_from_string(nil, '') }.to raise_error(RuntimeError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe MacOS::PlistHelpers, '#type_to_commandline_string' do
|
||||
context 'When given a certain data type' do
|
||||
it 'returns the required boolean entry type as a string' do
|
||||
expect(type_to_commandline_string(true)).to eq 'bool'
|
||||
end
|
||||
|
||||
it 'returns the required array entry type as a string' do
|
||||
expect(type_to_commandline_string(%w(foo bar))).to eq 'array'
|
||||
end
|
||||
|
||||
it 'returns the required dictionary entry type as a string' do
|
||||
expect(type_to_commandline_string('baz' => 'qux')).to eq 'dict'
|
||||
end
|
||||
|
||||
it 'returns the required string entry type as a string' do
|
||||
expect(type_to_commandline_string('quux')).to eq 'string'
|
||||
end
|
||||
|
||||
it 'returns the required integer entry type as a string' do
|
||||
expect(type_to_commandline_string(1)).to eq 'integer'
|
||||
end
|
||||
|
||||
it 'returns the required float entry type as a string' do
|
||||
expect(type_to_commandline_string(1.0)).to eq 'float'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe MacOS::PlistHelpers, '#convert_to_string_from_data_type' do
|
||||
context 'When given a certain data type' do
|
||||
it 'returns the required boolean entry' do
|
||||
expect(convert_to_string_from_data_type(true)).to eq 'bool true'
|
||||
end
|
||||
|
||||
# TODO: Skip until proper plist array syntax is implemented (i.e. containers)
|
||||
xit 'returns the required array entry' do
|
||||
expect(convert_to_string_from_data_type(%w(foo bar))).to eq 'array foo bar'
|
||||
end
|
||||
|
||||
# TODO: Skip until proper plist dict syntax is implemented (i.e. containers)
|
||||
xit 'returns the required dictionary entry' do
|
||||
expect(convert_to_string_from_data_type('baz' => 'qux')).to eq 'dict key value'
|
||||
end
|
||||
|
||||
it 'returns the required string entry' do
|
||||
expect(convert_to_string_from_data_type('quux')).to eq 'string quux'
|
||||
end
|
||||
|
||||
it 'returns the required integer entry' do
|
||||
expect(convert_to_string_from_data_type(1)).to eq 'integer 1'
|
||||
end
|
||||
|
||||
it 'returns the required float entry' do
|
||||
expect(convert_to_string_from_data_type(1.0)).to eq 'float 1.0'
|
||||
end
|
||||
end
|
||||
end
|
|
@ -5,13 +5,13 @@ describe 'macos::configurator' do
|
|||
before(:each) do
|
||||
stub_data_bag_item('credentials', 'apple_id').and_return(
|
||||
apple_id: 'developer@apple.com',
|
||||
password: 'apple_id_password')
|
||||
password: 'apple_id_password'
|
||||
)
|
||||
stub_command('which git').and_return('/usr/local/bin/git')
|
||||
stub_command('/usr/local/bin/mas account').and_return('developer@apple.com')
|
||||
end
|
||||
|
||||
let(:chef_run) do
|
||||
runner = ChefSpec::SoloRunner.new(platform: 'mac_os_x', version: '10.12')
|
||||
runner.converge(described_recipe)
|
||||
end
|
||||
let(:chef_run) { ChefSpec::SoloRunner.new.converge(described_recipe) }
|
||||
|
||||
it 'converges successfully' do
|
||||
expect { chef_run }.to_not raise_error
|
|
@ -2,10 +2,7 @@ require 'spec_helper'
|
|||
|
||||
describe 'macos::default' do
|
||||
context 'When all attributes are default, on macOS 10.12' do
|
||||
let(:chef_run) do
|
||||
runner = ChefSpec::ServerRunner.new(platform: 'mac_os_x', version: '10.12')
|
||||
runner.converge(described_recipe)
|
||||
end
|
||||
let(:chef_run) { ChefSpec::SoloRunner.new.converge(described_recipe) }
|
||||
|
||||
it 'converges successfully' do
|
||||
expect { chef_run }.to_not raise_error
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'macos::disable_software_updates' do
|
||||
context 'When all attributes are default, on macOS 10.12' do
|
||||
let(:chef_run) { ChefSpec::SoloRunner.new.converge(described_recipe) }
|
||||
|
||||
it 'converges successfully' do
|
||||
expect { chef_run }.to_not raise_error
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,10 +2,7 @@ require 'spec_helper'
|
|||
|
||||
describe 'macos::keep_awake' do
|
||||
context 'When all attributes are default, on macOS 10.12' do
|
||||
let(:chef_run) do
|
||||
runner = ChefSpec::ServerRunner.new(platform: 'mac_os_x', version: '10.12')
|
||||
runner.converge(described_recipe)
|
||||
end
|
||||
let(:chef_run) { ChefSpec::SoloRunner.new.converge(described_recipe) }
|
||||
|
||||
it 'converges successfully' do
|
||||
expect { chef_run }.to_not raise_error
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
*~
|
||||
*#
|
||||
.#*
|
||||
\#*#
|
||||
.*.sw[a-z]
|
||||
*.un~
|
||||
|
||||
# Bundler
|
||||
Gemfile
|
||||
Gemfile.lock
|
||||
bin/*
|
||||
.bundle/*
|
||||
|
||||
# test kitchen
|
||||
.kitchen/
|
||||
.kitchen.local.yml
|
||||
|
||||
# Chef
|
||||
Berksfile.lock
|
||||
.zero-knife.rb
|
||||
Policyfile.lock.json
|
||||
.autotest
|
||||
coverage
|
||||
.DS_Store
|
||||
pkg/*
|
||||
tags
|
||||
*/tags
|
||||
.chef
|
||||
results
|
||||
|
||||
# You should check in your Gemfile.lock in applications, and not in gems
|
||||
external_tests/*.lock
|
||||
/Gemfile.local
|
||||
|
||||
# ignore some common Bundler 'binstubs' directory names
|
||||
# http://gembundler.com/man/bundle-exec.1.html
|
||||
b/
|
||||
binstubs/
|
||||
.bundle
|
||||
# RVM and RBENV ruby version files
|
||||
.rbenv-version
|
||||
.rvmrc
|
||||
.ruby-version
|
||||
.ruby-gemset
|
||||
|
||||
# IDE files
|
||||
.project
|
||||
|
||||
# Documentation
|
||||
_site/*
|
||||
.yardoc/
|
||||
doc/
|
||||
|
||||
# Kitchen Tests Local Mode Data
|
||||
kitchen-tests/nodes/*
|
||||
|
||||
# Temporary files present during spec runs
|
||||
spec/data/test-dir
|
||||
spec/data/nodes
|
||||
/config/
|
||||
|
||||
# acceptance binstubs
|
||||
acceptance/bin/*
|
||||
|
||||
vendor/
|
||||
acceptance/vendor
|
||||
kitchen-tests/vendor
|
||||
|
||||
# Visual Studio Code files
|
||||
.vscode
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
.idea
|
||||
|
||||
# CMake
|
||||
cmake-build-debug/
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
||||
|
||||
# Testing
|
||||
*.box
|
||||
berks-cookbooks
|
||||
Vagrantfile
|
||||
.rubocop.yml
|
||||
.vagrant
|
||||
data_bags
|
|
@ -0,0 +1,36 @@
|
|||
---
|
||||
driver:
|
||||
name: vagrant
|
||||
provider: parallels
|
||||
|
||||
provisioner:
|
||||
name: chef_zero
|
||||
always_update_cookbooks: true
|
||||
|
||||
verifier:
|
||||
name: inspec
|
||||
sudo: true
|
||||
|
||||
platforms:
|
||||
- name: apex/macos-10.11.6
|
||||
- name: apex/macos-10.12.6
|
||||
- name: apex/macos-10.13
|
||||
|
||||
suites:
|
||||
- name: default
|
||||
run_list:
|
||||
- recipe[macos::keep_awake]
|
||||
- recipe[macos_test::new_users]
|
||||
- recipe[macos::disable_software_updates]
|
||||
- recipe[macos_test::preferences]
|
||||
- recipe[macos_test::machine_name]
|
||||
verifier:
|
||||
inspec_tests:
|
||||
- test/smoke/default
|
||||
|
||||
- name: xcode
|
||||
run_list:
|
||||
- recipe[macos_test::xcode]
|
||||
verifier:
|
||||
inspec_tests:
|
||||
- test/smoke/xcode
|
|
@ -0,0 +1,5 @@
|
|||
source 'https://supermarket.chef.io'
|
||||
|
||||
metadata
|
||||
|
||||
cookbook 'macos', path: './../../../'
|
|
@ -0,0 +1,4 @@
|
|||
name 'macos_test'
|
||||
version '1.0.1'
|
||||
|
||||
depends 'macos'
|
|
@ -0,0 +1 @@
|
|||
machine_name "New#{node['platform_version']}_Washing_Machine"
|
|
@ -0,0 +1,11 @@
|
|||
macos_user 'create admin user randall and enable automatic login' do
|
||||
username 'randall'
|
||||
password 'correct-horse-battery-staple'
|
||||
autologin true
|
||||
admin true
|
||||
end
|
||||
|
||||
macos_user 'create non-admin user johnny' do
|
||||
username 'johnny'
|
||||
password 'yang-yolked-cordon-karate'
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
plist 'show hidden files' do
|
||||
path '/Users/vagrant/Library/Preferences/com.apple.finder.plist'
|
||||
entry 'AppleShowAllFiles'
|
||||
value true
|
||||
end
|
||||
|
||||
plist 'put the Dock on the left side' do
|
||||
path '/Users/vagrant/Library/Preferences/com.apple.dock.plist'
|
||||
entry 'orientation'
|
||||
value 'left'
|
||||
end
|
||||
|
||||
plist 'disable window animations and Get Info animations' do
|
||||
path '/Users/vagrant/Library/Preferences/com.apple.dock.plist'
|
||||
entry 'DisableAllAnimations'
|
||||
value true
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
if node['platform_version'].match?(/10\.13/) || node['platform_version'].match?(/10\.12/)
|
||||
execute 'Disable Gatekeeper' do
|
||||
command 'spctl --master-disable'
|
||||
end
|
||||
|
||||
xcode '9.2' do
|
||||
ios_simulators %w(11 10)
|
||||
end
|
||||
|
||||
elsif node['platform_version'].match?(/10\.11/)
|
||||
|
||||
xcode '8.2.1' do
|
||||
ios_simulators %w(10 9)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
software_update_plist = '/Library/Preferences/com.apple.SoftwareUpdate.plist'
|
||||
automatic_check_enabled = 'AutomaticCheckEnabled'
|
||||
automatic_download = 'AutomaticDownload'
|
||||
|
||||
describe command("/usr/libexec/PlistBuddy -c 'Print :#{automatic_check_enabled}' #{software_update_plist}") do
|
||||
its('stdout') { should match('false') }
|
||||
end
|
||||
|
||||
describe command("/usr/libexec/PlistBuddy -c 'Print :#{automatic_download}' #{software_update_plist}") do
|
||||
its('stdout') { should match('false') }
|
||||
end
|
||||
|
||||
describe command("/usr/bin/defaults read-type #{software_update_plist} #{automatic_download}") do
|
||||
its('stdout') { should match('boolean') }
|
||||
end
|
||||
|
||||
describe command("/usr/bin/defaults read-type #{software_update_plist} #{automatic_check_enabled}") do
|
||||
its('stdout') { should match('boolean') }
|
||||
end
|
||||
|
||||
describe command("/usr/bin/defaults read #{software_update_plist} #{automatic_download}") do
|
||||
its('stdout') { should match('0') }
|
||||
end
|
||||
|
||||
describe command("/usr/bin/defaults read #{software_update_plist} #{automatic_check_enabled}") do
|
||||
its('stdout') { should match('0') }
|
||||
end
|
|
@ -0,0 +1,60 @@
|
|||
control 'power' do
|
||||
desc 'Display, machine and hard disk never sleep, the machine automatically restarts
|
||||
after a freeze or power outage, and Power Nap'
|
||||
|
||||
%w(sleep computersleep displaysleep harddisksleep).each do |setting|
|
||||
describe command("systemsetup -get#{setting}") do
|
||||
its('stdout') { should match('Never') }
|
||||
end
|
||||
end
|
||||
|
||||
%w(restartfreeze remoteappleevents).each do |setting|
|
||||
describe command("systemsetup -get#{setting}") do
|
||||
its('stdout') { should match('On') }
|
||||
end
|
||||
end
|
||||
|
||||
describe command('systemsetup -getwaitforstartupafterpowerfailure') do
|
||||
its('stdout') { should match('0 seconds') }
|
||||
end
|
||||
|
||||
describe command('pmset -g') do
|
||||
its('stdout') { should match(/sleep\s*0/) }
|
||||
its('stdout') { should match(/hibernatemode\s*0$/) }
|
||||
its('stdout') { should match(/ttyskeepawake\s*1/) }
|
||||
its('stdout') { should match(%r{hibernatefile\s*\/var\/vm\/sleepimage}) }
|
||||
its('stdout') { should match(/disksleep\s*0/) }
|
||||
its('stdout') { should match(/displaysleep\s*0/) }
|
||||
its('stdout') { should match(/ttyskeepawake\s*1/) }
|
||||
end
|
||||
|
||||
describe command("/usr/libexec/PlistBuddy -c 'Print :DarkWakeBackgroundTasks' '/Library/Preferences/com.apple.PowerManagement.plist'") do
|
||||
its('stdout') { should match('false') }
|
||||
end
|
||||
end
|
||||
|
||||
control 'screensaver' do
|
||||
desc 'screensaver is disabled'
|
||||
|
||||
def hardware_uuid
|
||||
system_profiler_hardware_output = `system_profiler SPHardwareDataType`
|
||||
hardware_overview = Psych.load(system_profiler_hardware_output)['Hardware']['Hardware Overview']
|
||||
hardware_overview['Hardware UUID']
|
||||
end
|
||||
|
||||
describe command("/usr/libexec/PlistBuddy -c 'Print :idleTime' /Users/vagrant/Library/Preferences/ByHost/com.apple.screensaver.#{hardware_uuid}.plist"), :skip do
|
||||
its('stdout') { should match(/0/) }
|
||||
end
|
||||
|
||||
describe command('su vagrant -c "/usr/bin/defaults -currentHost read com.apple.screensaver idleTime"') do
|
||||
its('stdout') { should match(/0/) }
|
||||
end
|
||||
|
||||
describe command('su vagrant -c "/usr/bin/defaults -currentHost read-type com.apple.screensaver idleTime"') do
|
||||
its('stdout') { should match(/integer/) }
|
||||
end
|
||||
|
||||
describe command("file --brief --mime /Users/vagrant/Library/Preferences/ByHost/com.apple.screensaver.#{hardware_uuid}.plist"), :skip do
|
||||
its('stdout') { should match(/binary/) }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
control 'machine name' do
|
||||
desc 'machine name is set to the format "New#{macos_semantic_version}_Washing_Machine"'
|
||||
|
||||
macos_semantic_version = command('sw_vers -productVersion').stdout.strip
|
||||
hostname_pattern = /New#{macos_semantic_version}_Washing_Machine/
|
||||
|
||||
hostname_commands = ['hostname',
|
||||
'scutil --get ComputerName',
|
||||
'scutil --get HostName']
|
||||
|
||||
hostname_commands.each do |hostname_command|
|
||||
describe command(hostname_command) do
|
||||
its('stdout') { should match hostname_pattern }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
control 'new macOS users' do
|
||||
desc 'they exist with the expected characteristics'
|
||||
|
||||
describe user('randall') do
|
||||
it { should exist }
|
||||
its('uid') { should eq 503 }
|
||||
its('gid') { should eq 20 }
|
||||
its('home') { should eq '/Users/randall' }
|
||||
end
|
||||
|
||||
describe user('johnny') do
|
||||
it { should exist }
|
||||
its('uid') { should eq 504 }
|
||||
its('gid') { should eq 20 }
|
||||
its('home') { should eq '/Users/johnny' }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
control 'macOS user preferences' do
|
||||
desc 'they are set to the correct values'
|
||||
|
||||
describe command("/usr/libexec/PlistBuddy -c 'Print :orientation' /Users/vagrant/Library/Preferences/com.apple.dock.plist") do
|
||||
its('stdout') { should match 'left' }
|
||||
end
|
||||
|
||||
describe command("/usr/libexec/PlistBuddy -c 'Print :AppleShowAllFiles' /Users/vagrant/Library/Preferences/com.apple.finder.plist") do
|
||||
its('stdout') { should match 'true' }
|
||||
end
|
||||
|
||||
describe command("/usr/libexec/PlistBuddy -c 'Print :DisableAllAnimations' /Users/vagrant/Library/Preferences/com.apple.dock.plist") do
|
||||
its('stdout') { should match 'true' }
|
||||
end
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
control 'xcode' do
|
||||
desc 'application Xcode exists and Developer mode is enabled'
|
||||
|
||||
describe file('/Applications/Xcode.app') do
|
||||
it { should exist }
|
||||
it { should be_symlink }
|
||||
end
|
||||
|
||||
if os[:release].match?(/10\.13/) || os[:release].match?(/10\.12/)
|
||||
describe directory('/Applications/Xcode-9.2.app') do
|
||||
it { should exist }
|
||||
end
|
||||
|
||||
describe command('/opt/chef/embedded/bin/xcversion simulators') do
|
||||
its('stdout') { should match(/iOS 10\.3\.1 Simulator \(installed\)/) }
|
||||
end
|
||||
|
||||
elsif os[:release].match?(/10\.11/)
|
||||
describe directory('/Applications/Xcode-8.2.1.app') do
|
||||
it { should exist }
|
||||
end
|
||||
|
||||
describe command('/opt/chef/embedded/bin/xcversion simulators') do
|
||||
its('stdout') { should match(/iOS 9\.3 Simulator \(installed\)/) }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,16 +0,0 @@
|
|||
control 'xcode' do
|
||||
desc 'application Xcode exists and Developer mode is enabled'
|
||||
|
||||
describe file '/Applications/Xcode.app' do
|
||||
it { should exist }
|
||||
it { should be_symlink }
|
||||
end
|
||||
|
||||
describe directory '/Applications/Xcode-9.app' do
|
||||
it { should exist }
|
||||
end
|
||||
|
||||
describe command('/usr/local/bin/xcversion simulators') do
|
||||
its('stdout') { should match /iOS 10\.3\.1 Simulator \(installed\)/ }
|
||||
end
|
||||
end
|
Загрузка…
Ссылка в новой задаче