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:
Jacob Zaval 2022-03-11 00:06:44 -08:00 коммит произвёл GitHub
Родитель 5cf741454e 6c0636f733
Коммит b252d2f4e6
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
12 изменённых файлов: 96 добавлений и 97 удалений

Просмотреть файл

@ -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**

Просмотреть файл

@ -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