Merge pull request #254 from microsoft/feature/xcode
update `xcode` to not use attributes, only auth dev account if out-of-date
This commit is contained in:
Коммит
b252d2f4e6
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -4,30 +4,34 @@
|
|||
|
||||
### Fixed
|
||||
|
||||
- Enabled `macos_user` resource to parse `sysadminctl` stderr, resolving[Bug 197](https://github.com/microsoft/macos-cookbook/issues/197).
|
||||
- Reversed order of arguments for certificate installation to address [Bug 244](https://github.com/microsoft/macos-cookbook/issues/244).
|
||||
- Extracted authentication with Apple via `xcode` resource away `xcode` object instantiation, resolving [Bug #234](https://github.com/microsoft/macos-cookbook/issues/234).
|
||||
- Enabled `macos_user` resource to parse `sysadminctl` stderr, resolving [Bug 197](https://github.com/microsoft/macos-cookbook/issues/197).
|
||||
- Reversed order of arguments for certificate installation, resolving [Bug 244](https://github.com/microsoft/macos-cookbook/issues/244).
|
||||
|
||||
### Added
|
||||
|
||||
- New test suites and recipe change to account for `.cer` files.
|
||||
- New certificate resource property: `kc_passwd` which allows setting of keychain password.
|
||||
- Check for certificate existence within the keychain before installing a new one to ensure idempotency.
|
||||
- Made password properties sensitive.
|
||||
- Added `apple_id` property to `xcode` resource to remove dependency on attributes or data bags for authentication.
|
||||
- New test suites and recipe change to account for `.cer` files.
|
||||
- New certificate resource property: `kc_passwd` which allows setting of keychain password.
|
||||
- Check for certificate existence within the keychain before installing a new one to ensure idempotency.
|
||||
- Made password properties sensitive.
|
||||
- Updated certificate resource documentation.
|
||||
- Secure token support for `macos_user` resource via new properties `secure_token` and `existing_token_auth`.
|
||||
- New unit and integration tests for `macos_user` resource.
|
||||
|
||||
### Changed
|
||||
|
||||
- Removed dependency on using the `default['macos']['admin_password']` attribute for setting the keychain password when using the certificate resource.
|
||||
- Unified `macos_user` test suites.
|
||||
- Updated `macos_user` resource to use not utilize default attributes for authorization.
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed the ability to authenticate with Apple for `xcode` downloads via node attributes or data bags.
|
||||
- Removed dependency on using the `default['macos']['admin_password']` attribute for setting the keychain password when using the certificate resource.
|
||||
- Removed last default cookbook attributes:
|
||||
- `node['macos']['admin_user']`
|
||||
- `node['macos']['admin_password']`
|
||||
- `node['macos']['apple_id']`
|
||||
|
||||
## [4.2.3] - 2022-02-03
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
default['macos']['apple_id'] = nil
|
|
@ -1,5 +1,4 @@
|
|||
xcode
|
||||
=====
|
||||
# xcode
|
||||
|
||||
Use the **xcode** resource to manage a single installation of Apple's Xcode IDE.
|
||||
The [**xcode**](https://github.com/Microsoft/macos-cookbook/blob/master/resources/xcode.rb) resource manages the state of a single Xcode installation
|
||||
|
@ -11,17 +10,17 @@ your developer credentials. Be sure to only provide the semantic version (e.g.
|
|||
`path` will move an existing Xcode installation of the requested version to that
|
||||
path, overwriting an existing bundle if it is not the requested version.
|
||||
|
||||
|
||||
Syntax
|
||||
------
|
||||
## Syntax
|
||||
|
||||
The simplest use of the **xcode** resource is:
|
||||
|
||||
```ruby
|
||||
xcode '9.4.1'
|
||||
xcode '9.4.1' do
|
||||
apple_id({ username: 'user@example', password: 'placeholder' })
|
||||
end
|
||||
```
|
||||
|
||||
which would install Xcode 9.4.1 with the included iOS simulators.
|
||||
which would install Xcode 9.4.1 directly from Apple with the included iOS simulators.
|
||||
|
||||
The full syntax for all of the properties that are available to the **xcode**
|
||||
resource is:
|
||||
|
@ -30,14 +29,15 @@ resource is:
|
|||
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 current iOS simulators if not specified
|
||||
apple_id Hash # required unless a `download_url` is provided
|
||||
download_url String # defaults to empty if not specified
|
||||
ios_simulators Array # defaults to current iOS simulators if not specified
|
||||
action Symbol # defaults to [:install_gem, :install_xcode, :install_simulators] if not specified
|
||||
end
|
||||
```
|
||||
|
||||
Actions
|
||||
-------
|
||||
## Actions
|
||||
|
||||
It is recommended to use the default action of all three actions, since the
|
||||
`xcode-install` gem is required to use the resource. Only use actions independently
|
||||
if you're going to manage this dependency on your own.
|
||||
|
@ -64,39 +64,17 @@ of iOS simulators declared in `ios_simulators`.
|
|||
|
||||
In order to install Xcode directly from Apple, you'll need to provide a AppleID for an active developer account. There are two methods to do so:
|
||||
|
||||
The `xcode` resource can utilize a `credentials` data bag with an `apple_id` data bag item.
|
||||
|
||||
**Example:**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "apple_id",
|
||||
"apple_id": "farva@spurbury.gov",
|
||||
"password": "0k@yN0cR34m"
|
||||
}
|
||||
```
|
||||
|
||||
The `xcode` resource can also utilize an AppleID set (preferably at run-time for
|
||||
security, and unset after use) under the node attributes
|
||||
`node['macos']['apple_id']['user']` and `node['macos']['apple_id']['password']`.
|
||||
The `xcode` `apple_id` property can take a hash in the form of `{ username: 'user@example', password: 'placeholder' }`. This will use the user and password to authenticate with Apple. We recommend interpolating these values into your recipe using your secret management system of choice. If you would prefer to utilize environment variables on the system, set 'XCODE_INSTALL_USER' and 'XCODE_INSTALL_PASSWORD' instead.
|
||||
|
||||
**Example:**
|
||||
|
||||
```ruby
|
||||
node.normal['macos']['apple_id']['user'] = 'farva@spurbury.gov'
|
||||
node.normal['macos']['apple_id']['password'] = '0k@yN0cR34m'
|
||||
|
||||
xcode '10.1'
|
||||
|
||||
ruby_block 'Remove AppleID password attribute' do
|
||||
block do
|
||||
node.rm('macos', 'apple_id', 'password')
|
||||
end
|
||||
xcode '9.4.1' do
|
||||
apple_id({ username: 'user@example', password: 'placeholder' })
|
||||
end
|
||||
```
|
||||
|
||||
Examples
|
||||
--------
|
||||
## Examples
|
||||
|
||||
**Install different versions of Xcode based on platform version node attributes**
|
||||
|
||||
|
|
10
kitchen.yml
10
kitchen.yml
|
@ -79,6 +79,10 @@ suites:
|
|||
- name: xcode-from-apple
|
||||
run_list:
|
||||
- recipe[macos_test::xcode_from_apple]
|
||||
lifecycle:
|
||||
pre_converge:
|
||||
- remote: echo -n '<%= ENV['APPLEID_USERNAME'] %>' > ~/username
|
||||
- remote: echo -n '<%= ENV['APPLEID_PASSWORD'] %>' > ~/password
|
||||
verifier:
|
||||
controls:
|
||||
- xcode-and-simulators
|
||||
|
@ -86,9 +90,9 @@ suites:
|
|||
- name: xcode-from-url
|
||||
run_list:
|
||||
- recipe[macos_test::xcode_from_url]
|
||||
attributes:
|
||||
xcode:
|
||||
download_url: <%= ENV['XCODE_URL'] %>
|
||||
lifecycle:
|
||||
pre_converge:
|
||||
- remote: echo -n '<%= ENV['XCODE_URL'] %>' > ~/xcode
|
||||
verifier:
|
||||
controls:
|
||||
- xcode-and-simulators
|
||||
|
|
|
@ -5,33 +5,36 @@ module MacOS
|
|||
class DeveloperAccount
|
||||
attr_reader :credentials
|
||||
|
||||
def initialize(data_bag_retrieval, node_credential_attributes, download_url)
|
||||
def initialize(apple_id_hash, download_url)
|
||||
if download_url.empty?
|
||||
developer_id = find_apple_id(data_bag_retrieval, node_credential_attributes)
|
||||
developer_id = validate_apple_id(apple_id_hash)
|
||||
@credentials = {
|
||||
XCODE_INSTALL_USER: developer_id['apple_id'],
|
||||
XCODE_INSTALL_PASSWORD: developer_id['password'],
|
||||
XCODE_INSTALL_USER: developer_id[:username],
|
||||
XCODE_INSTALL_PASSWORD: developer_id[:password],
|
||||
}
|
||||
authenticate_with_apple(@credentials)
|
||||
end
|
||||
end
|
||||
|
||||
def validate_apple_id(apple_id_hash)
|
||||
if apple_id_hash.values.any?
|
||||
{
|
||||
username: apple_id_hash[:username],
|
||||
password: apple_id_hash[:password],
|
||||
}
|
||||
elsif ENV['XCODE_INSTALL_USER'] && ENV['XCODE_INSTALL_PASSWORD']
|
||||
{
|
||||
username: ENV['XCODE_INSTALL_USER'],
|
||||
password: ENV['XCODE_INSTALL_PASSWORD'],
|
||||
}
|
||||
else
|
||||
raise('Developer credentials not supplied, and a URL was not provided for Xcode!')
|
||||
end
|
||||
end
|
||||
|
||||
def authenticate_with_apple(credentials)
|
||||
shell_out!(XCVersion.update, env: credentials)
|
||||
end
|
||||
|
||||
def find_apple_id(data_bag_retrieval, node_credential_attributes)
|
||||
if node_credential_attributes
|
||||
{
|
||||
'apple_id' => node_credential_attributes['user'],
|
||||
'password' => node_credential_attributes['password'],
|
||||
}
|
||||
else
|
||||
data_bag_retrieval.call
|
||||
end
|
||||
rescue Net::HTTPServerException
|
||||
raise('Developer credentials not supplied, and a URL was not provided for Xcode!')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -3,18 +3,20 @@ include MacOS
|
|||
|
||||
module MacOS
|
||||
class Xcode
|
||||
attr_reader :version
|
||||
attr_reader :intended_path
|
||||
attr_reader :download_url
|
||||
attr_reader :version, :intended_path, :download_url
|
||||
|
||||
def initialize(semantic_version, intended_path, download_url = '')
|
||||
@semantic_version = semantic_version
|
||||
@intended_path = intended_path
|
||||
@download_url = download_url
|
||||
@version = if download_url.empty?
|
||||
latest_xcode_revision(Xcode::Version.new(semantic_version))
|
||||
@version = -> { determine_version }
|
||||
end
|
||||
|
||||
def determine_version
|
||||
@version = if @download_url.empty?
|
||||
latest_xcode_revision(Xcode::Version.new(@semantic_version))
|
||||
else
|
||||
semantic_version
|
||||
@semantic_version
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ module MacOS
|
|||
end
|
||||
|
||||
def install_xcode(xcode)
|
||||
xcversion "install '#{xcode.version}' #{download_url_option(xcode)}".strip
|
||||
xcversion "install '#{xcode.version.call}' #{download_url_option(xcode)}".strip
|
||||
end
|
||||
|
||||
def installed_xcodes
|
||||
|
|
|
@ -7,6 +7,7 @@ property :version, String, name_property: true
|
|||
property :path, String, default: '/Applications/Xcode.app'
|
||||
property :ios_simulators, Array
|
||||
property :download_url, String, default: ''
|
||||
property :apple_id, Hash
|
||||
|
||||
action :install_gem do
|
||||
command_line_tools 'latest'
|
||||
|
@ -24,12 +25,6 @@ action :install_gem do
|
|||
end
|
||||
|
||||
action :install_xcode do
|
||||
developer = DeveloperAccount.new(
|
||||
-> { data_bag_item(:credentials, :apple_id) },
|
||||
node['macos']['apple_id'],
|
||||
new_resource.download_url
|
||||
)
|
||||
|
||||
xcode = Xcode.new(
|
||||
new_resource.version,
|
||||
new_resource.path,
|
||||
|
@ -38,7 +33,7 @@ action :install_xcode do
|
|||
|
||||
unless xcode.compatible_with_platform?(node['platform_version'])
|
||||
ruby_block 'exception' do
|
||||
raise("Xcode #{xcode.version} not supported on #{node['platform_version']}")
|
||||
raise("Xcode #{new_resource.version} not supported on #{node['platform_version']}")
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -49,7 +44,8 @@ action :install_xcode do
|
|||
not_if { xcode.installed? }
|
||||
end
|
||||
|
||||
execute "install Xcode #{xcode.version}" do
|
||||
execute "install Xcode #{new_resource.version}" do
|
||||
developer = DeveloperAccount.new(new_resource.apple_id, new_resource.download_url)
|
||||
command XCVersion.install_xcode(xcode)
|
||||
environment developer.credentials
|
||||
cwd '/Users/Shared'
|
||||
|
|
|
@ -90,48 +90,57 @@ describe MacOS::Xcode do
|
|||
end
|
||||
it 'returns the name of the latest Xcode 12 beta when initialized with the semantic version' do
|
||||
xcode = MacOS::Xcode.new('12.0', '/Applications/Xcode.app')
|
||||
xcode.version.call
|
||||
expect(xcode.version).to eq '12 beta 4'
|
||||
expect(xcode.version).to_not eq '12 beta'
|
||||
expect(xcode.version).to_not eq '12 for macOS Universal Apps beta'
|
||||
end
|
||||
it 'returns the name of the latest Xcode 11.6 beta when initialized with the semantic version' do
|
||||
xcode = MacOS::Xcode.new('11.6', '/Applications/Xcode.app')
|
||||
xcode.version.call
|
||||
expect(xcode.version).to eq '11.6 beta 2'
|
||||
expect(xcode.version).to_not eq '11.6 beta'
|
||||
expect(xcode.version).to_not eq '11.6 beta2'
|
||||
end
|
||||
it 'returns the name of Xcode 11.5 official when initialized with the semantic version' do
|
||||
xcode = MacOS::Xcode.new('11.5', '/Applications/Xcode.app')
|
||||
xcode.version.call
|
||||
expect(xcode.version).to eq '11.5'
|
||||
expect(xcode.version).to_not eq '11.5 beta'
|
||||
expect(xcode.version).to_not eq '11.5 Release Candidate'
|
||||
end
|
||||
it 'returns the name of Xcode 10 official when initialized with the semantic version' do
|
||||
xcode = MacOS::Xcode.new('10.0', '/Applications/Xcode.app')
|
||||
xcode.version.call
|
||||
expect(xcode.version).to eq '10'
|
||||
expect(xcode.version).to_not eq '10 Release Candidate'
|
||||
expect(xcode.version).to_not eq '10 beta 1'
|
||||
end
|
||||
it 'returns the name of Xcode 9.4.2 official when initialized with the semantic version' do
|
||||
xcode = MacOS::Xcode.new('9.4.2', '/Applications/Xcode.app')
|
||||
xcode.version.call
|
||||
expect(xcode.version).to eq '9.4.2'
|
||||
expect(xcode.version).to_not eq '9.4.2 beta 2'
|
||||
expect(xcode.version).to_not eq '9.4.2 beta 3'
|
||||
end
|
||||
it 'returns the temporary beta path set by xcversion when initialized with the semantic version' do
|
||||
xcode = MacOS::Xcode.new('9.4.2', '/Applications/Xcode.app')
|
||||
xcode.version.call
|
||||
expect(xcode.current_path).to eq '/Applications/Xcode-9.4.2.app'
|
||||
end
|
||||
it 'returns the name of Xcode 9.3 when initialized with the semantic version' do
|
||||
xcode = MacOS::Xcode.new('9.3', '/Applications/Xcode.app')
|
||||
xcode.version.call
|
||||
expect(xcode.version).to eq '9.3'
|
||||
end
|
||||
it 'returns the name of Xcode 9 when initialized with the semantic version' do
|
||||
xcode = MacOS::Xcode.new('9.0', '/Applications/Xcode.app')
|
||||
xcode.version.call
|
||||
expect(xcode.version).to eq '9'
|
||||
end
|
||||
it 'returns the name of Xcode 8.3.3 when initialized with the semantic version' do
|
||||
xcode = MacOS::Xcode.new('8.3.3', '/Applications/Xcode.app')
|
||||
xcode.version.call
|
||||
expect(xcode.version).to eq '8.3.3'
|
||||
end
|
||||
it 'correctly determines platform compatibility for Xcode 11' do
|
||||
|
@ -203,11 +212,13 @@ describe MacOS::Xcode do
|
|||
|
||||
it 'ignores the Apple version list and uses the provided version' do
|
||||
xcode = MacOS::Xcode.new('0.0', '/Applications/Xcode.app', 'https://www.apple.com')
|
||||
xcode.version.call
|
||||
expect(xcode.version).to eq '0.0'
|
||||
end
|
||||
|
||||
it 'ignores the Apple version list and uses the provided version' do
|
||||
xcode = MacOS::Xcode.new('2', '/Applications/Xcode.app', 'https://www.apple.com')
|
||||
xcode.version.call
|
||||
expect(xcode.version).to eq '2'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,10 +4,9 @@ describe 'xcode' do
|
|||
step_into :xcode
|
||||
platform 'mac_os_x'
|
||||
|
||||
default_attributes['macos']['apple_id']['user'] = 'developer@apple.com'
|
||||
default_attributes['macos']['apple_id']['password'] = 'apple_id_password'
|
||||
|
||||
before(:each) do
|
||||
allow_any_instance_of(MacOS::DeveloperAccount).to receive(:validate_apple_id)
|
||||
.and_return({ username: 'user@example', password: 'placeholder' })
|
||||
allow_any_instance_of(MacOS::DeveloperAccount).to receive(:authenticate_with_apple)
|
||||
.and_return(true)
|
||||
allow(MacOS::XCVersion).to receive(:available_versions)
|
||||
|
@ -109,7 +108,7 @@ describe 'xcode' do
|
|||
end
|
||||
end
|
||||
|
||||
it { is_expected.to run_execute('install Xcode 10') }
|
||||
it { is_expected.to run_execute('install Xcode 10.0') }
|
||||
it { is_expected.to delete_link('/Applications/Xcode.app') }
|
||||
|
||||
it { is_expected.to run_execute('move /Applications/Xcode-10.app to /Applications/Xcode.app') }
|
||||
|
@ -129,7 +128,7 @@ describe 'xcode' do
|
|||
end
|
||||
end
|
||||
|
||||
it { is_expected.to run_execute('install Xcode 11.6 beta') }
|
||||
it { is_expected.to run_execute('install Xcode 11.6') }
|
||||
it { is_expected.to delete_link('/Applications/Xcode.app') }
|
||||
|
||||
it {
|
||||
|
@ -224,7 +223,7 @@ describe 'xcode' do
|
|||
end
|
||||
end
|
||||
|
||||
it { is_expected.not_to run_execute('install Xcode 10') }
|
||||
it { is_expected.not_to run_execute('install Xcode 10.0') }
|
||||
it { is_expected.not_to delete_link('/Applications/Xcode.app') }
|
||||
|
||||
it { is_expected.to run_execute('move /Applications/Some_Weird_Path.app to /Applications/Chef_Managed_Xcode.app') }
|
||||
|
@ -244,7 +243,7 @@ describe 'xcode' do
|
|||
end
|
||||
end
|
||||
|
||||
it { is_expected.to run_execute('install Xcode 10') }
|
||||
it { is_expected.to run_execute('install Xcode 10.0') }
|
||||
it { is_expected.to delete_link('/Applications/Xcode.app') }
|
||||
|
||||
it { is_expected.to run_execute('move /Applications/Xcode-10.app to /Applications/Xcode.app') }
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
if node['platform_version'] >= '10.15.2'
|
||||
xcode '11.5'
|
||||
if node['platform_version'] >= '10.15.6'
|
||||
xcode '12.0' do
|
||||
apple_id({ username: ::File.read('/Users/vagrant/username'), password: ::File.read('/Users/vagrant/password') })
|
||||
end
|
||||
else
|
||||
xcode '9.4.1' do
|
||||
ios_simulators ['11', '10']
|
||||
xcode '10.2.1' do
|
||||
ios_simulators ['12', '11']
|
||||
apple_id({ username: ::File.read('/Users/vagrant/username'), password: ::File.read('/Users/vagrant/password') })
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
if node['platform_version'] >= '10.14.4'
|
||||
xcode 'installs 11.0' do
|
||||
download_url node['xcode']['download_url']
|
||||
version '11.0'
|
||||
if node['platform_version'] >= '10.15.6'
|
||||
xcode 'installs 12.0' do
|
||||
download_url ::File.read('/Users/vagrant/xcode')
|
||||
version '12.0'
|
||||
end
|
||||
else
|
||||
xcode 'installs 9.4.1' do
|
||||
download_url node['xcode']['download_url']
|
||||
version '9.4.1'
|
||||
xcode 'installs 10.2.1' do
|
||||
download_url ::File.read('/Users/vagrant/xcode')
|
||||
version '10.2.1'
|
||||
end
|
||||
end
|
||||
|
|
Загрузка…
Ссылка в новой задаче