Commit da257d32 authored by Avi Drissman's avatar Avi Drissman Committed by Commit Bot

Make the .pkg non-relocatable

The .pkg installer should always install Google Chrome into
/Applications, rather than find an installation of Google Chrome
located somewhere around the user's hard drive and update it. Mark the
component package as non-relocatable so that this happens.

Bug: 1067275
Change-Id: Idedb79e544f38e44661a545ee9dbc7000ee31d24
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2135051
Auto-Submit: Avi Drissman <avi@chromium.org>
Commit-Queue: Robert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Cr-Commit-Position: refs/heads/master@{#756668}
parent 8f16e273
...@@ -10,6 +10,7 @@ The pipeline module orchestrates the entire signing process, which includes: ...@@ -10,6 +10,7 @@ The pipeline module orchestrates the entire signing process, which includes:
""" """
import os.path import os.path
import plistlib
from . import commands, model, modification, notarize, parts, signing from . import commands, model, modification, notarize, parts, signing
...@@ -143,6 +144,33 @@ def _create_pkgbuild_scripts(paths, dist_config): ...@@ -143,6 +144,33 @@ def _create_pkgbuild_scripts(paths, dist_config):
return scripts_path return scripts_path
def _component_property_path(paths, dist_config):
"""Creates a component plist file for use by `pkgbuild`. The reason this
file is used is to ensure that the component package is not relocatable. See
https://scriptingosx.com/2017/05/relocatable-package-installers-and-quickpkg-update/
for information on why that's important.
Args:
paths: A |model.Paths| object.
dist_config: The |config.CodeSignConfig| object.
Returns:
The path to the component plist file.
"""
component_property_path = os.path.join(
paths.work, '{}.plist'.format(dist_config.app_product))
plistlib.writePlist([{
'BundleHasStrictIdentifier': True,
'BundleIsRelocatable': False,
'BundleIsVersionChecked': True,
'BundleOverwriteAction': 'upgrade',
'RootRelativeBundlePath': dist_config.app_dir
}], component_property_path)
return component_property_path
def _productbuild_distribution_path(paths, dist_config, component_pkg_path): def _productbuild_distribution_path(paths, dist_config, component_pkg_path):
"""Creates a distribution XML file for use by `productbuild`. This specifies """Creates a distribution XML file for use by `productbuild`. This specifies
that an x64 machine is required, and copies the OS requirement from the copy that an x64 machine is required, and copies the OS requirement from the copy
...@@ -225,6 +253,15 @@ def _package_and_sign_pkg(paths, dist_config): ...@@ -225,6 +253,15 @@ def _package_and_sign_pkg(paths, dist_config):
## The component package. ## The component package.
# Because the component package is built using the --root option, copy the
# .app into a directory by itself, as `pkgbuild` archives the entire
# directory specified as the root directory.
root_directory = os.path.join(paths.work, 'payload')
commands.make_dir(root_directory)
app_path = os.path.join(paths.work, dist_config.app_dir)
new_app_path = os.path.join(root_directory, dist_config.app_dir)
commands.copy_files(app_path, root_directory)
# The spaces are removed from |dist_config.app_product| for the component # The spaces are removed from |dist_config.app_product| for the component
# package path due to a bug in Installer.app that causes the "Show Files" # package path due to a bug in Installer.app that causes the "Show Files"
# window to be blank if there is a space in a component package name. # window to be blank if there is a space in a component package name.
...@@ -232,14 +269,14 @@ def _package_and_sign_pkg(paths, dist_config): ...@@ -232,14 +269,14 @@ def _package_and_sign_pkg(paths, dist_config):
component_pkg_name = '{}.pkg'.format(dist_config.app_product).replace( component_pkg_name = '{}.pkg'.format(dist_config.app_product).replace(
' ', '') ' ', '')
component_pkg_path = os.path.join(paths.work, component_pkg_name) component_pkg_path = os.path.join(paths.work, component_pkg_name)
app_path = os.path.join(paths.work, dist_config.app_dir) component_property_path = _component_property_path(paths, dist_config)
scripts_path = _create_pkgbuild_scripts(paths, dist_config) scripts_path = _create_pkgbuild_scripts(paths, dist_config)
commands.run_command([ commands.run_command([
'pkgbuild', '--identifier', dist_config.base_bundle_id, '--version', 'pkgbuild', '--root', root_directory, '--component-plist',
dist_config.version, '--component', app_path, '--install-location', component_property_path, '--identifier', dist_config.base_bundle_id,
'/Applications', '--scripts', scripts_path, component_pkg_path '--version', dist_config.version, '--install-location', '/Applications',
'--scripts', scripts_path, component_pkg_path
]) ])
## The product archive. ## The product archive.
......
...@@ -19,6 +19,10 @@ def _get_work_dir(*args, **kwargs): ...@@ -19,6 +19,10 @@ def _get_work_dir(*args, **kwargs):
_get_work_dir.count = 0 _get_work_dir.count = 0
def _component_property_path(paths, dist_config):
return '$W/App Product.plist'
def _productbuild_distribution_path(p, d, c): def _productbuild_distribution_path(p, d, c):
return '$W/App Product.dist' return '$W/App Product.dist'
...@@ -31,6 +35,17 @@ def _read_plist(p): ...@@ -31,6 +35,17 @@ def _read_plist(p):
return {'LSMinimumSystemVersion': '10.19.7'} return {'LSMinimumSystemVersion': '10.19.7'}
def _write_plist(d, p):
_write_plist.contents = d
_write_plist.contents = ''
def _last_written_plist():
return _write_plist.contents
def _read_file(p): def _read_file(p):
if p == '$I/Product Packaging/pkg_postinstall.in': if p == '$I/Product Packaging/pkg_postinstall.in':
return """app dir is '@APP_DIR@' return """app dir is '@APP_DIR@'
...@@ -294,6 +309,27 @@ brand code is 'MOO' ...@@ -294,6 +309,27 @@ brand code is 'MOO'
framework dir is 'App Product.app/Contents/Frameworks/Product Framework.framework'""" framework dir is 'App Product.app/Contents/Frameworks/Product Framework.framework'"""
) )
@mock.patch('signing.pipeline.plistlib.writePlist', _write_plist)
def test_component_property_path(self, **kwargs):
manager = mock.Mock()
for attr in kwargs:
manager.attach_mock(kwargs[attr], attr)
dist = model.Distribution()
dist_config = dist.to_config(test_config.TestConfig())
paths = self.paths.replace_work('$W')
self.assertEqual('$W/App Product.plist',
pipeline._component_property_path(paths, dist_config))
self.assertEqual(_last_written_plist(), [{
'BundleOverwriteAction': 'upgrade',
'BundleIsVersionChecked': True,
'BundleHasStrictIdentifier': True,
'RootRelativeBundlePath': 'App Product.app',
'BundleIsRelocatable': False
}])
@mock.patch('signing.commands.plistlib.readPlist', _read_plist) @mock.patch('signing.commands.plistlib.readPlist', _read_plist)
def test_productbuild_distribution_path(self, **kwargs): def test_productbuild_distribution_path(self, **kwargs):
manager = mock.Mock() manager = mock.Mock()
...@@ -358,6 +394,8 @@ framework dir is 'App Product.app/Contents/Frameworks/Product Framework.framewor ...@@ -358,6 +394,8 @@ framework dir is 'App Product.app/Contents/Frameworks/Product Framework.framewor
self.assertEqual('AppProduct-99.0.9999.99', self.assertEqual('AppProduct-99.0.9999.99',
kwargs['sign_part'].mock_calls[0][1][2].identifier) kwargs['sign_part'].mock_calls[0][1][2].identifier)
@mock.patch('signing.pipeline._component_property_path',
_component_property_path)
@mock.patch('signing.pipeline._productbuild_distribution_path', @mock.patch('signing.pipeline._productbuild_distribution_path',
_productbuild_distribution_path) _productbuild_distribution_path)
@mock.patch('signing.pipeline._create_pkgbuild_scripts', @mock.patch('signing.pipeline._create_pkgbuild_scripts',
...@@ -386,12 +424,14 @@ framework dir is 'App Product.app/Contents/Frameworks/Product Framework.framewor ...@@ -386,12 +424,14 @@ framework dir is 'App Product.app/Contents/Frameworks/Product Framework.framewor
pkgbuild_args = run_commands[0][1][0] pkgbuild_args = run_commands[0][1][0]
productbuild_args = run_commands[1][1][0] productbuild_args = run_commands[1][1][0]
self.assertEqual('$W/payload',
_get_adjacent_item(pkgbuild_args, '--root'))
self.assertEqual('$W/App Product.plist',
_get_adjacent_item(pkgbuild_args, '--component-plist'))
self.assertEqual('test.signing.bundle_id', self.assertEqual('test.signing.bundle_id',
_get_adjacent_item(pkgbuild_args, '--identifier')) _get_adjacent_item(pkgbuild_args, '--identifier'))
self.assertEqual('99.0.9999.99', self.assertEqual('99.0.9999.99',
_get_adjacent_item(pkgbuild_args, '--version')) _get_adjacent_item(pkgbuild_args, '--version'))
self.assertEqual('$W/App Product.app',
_get_adjacent_item(pkgbuild_args, '--component'))
self.assertEqual('$W/scripts', self.assertEqual('$W/scripts',
_get_adjacent_item(pkgbuild_args, '--scripts')) _get_adjacent_item(pkgbuild_args, '--scripts'))
...@@ -439,6 +479,8 @@ framework dir is 'App Product.app/Contents/Frameworks/Product Framework.framewor ...@@ -439,6 +479,8 @@ framework dir is 'App Product.app/Contents/Frameworks/Product Framework.framewor
self.assertEqual('AppProduct-99.0.9999.99-MOO', self.assertEqual('AppProduct-99.0.9999.99-MOO',
kwargs['sign_part'].mock_calls[0][1][2].identifier) kwargs['sign_part'].mock_calls[0][1][2].identifier)
@mock.patch('signing.pipeline._component_property_path',
_component_property_path)
@mock.patch('signing.pipeline._productbuild_distribution_path', @mock.patch('signing.pipeline._productbuild_distribution_path',
_productbuild_distribution_path) _productbuild_distribution_path)
@mock.patch('signing.pipeline._create_pkgbuild_scripts', @mock.patch('signing.pipeline._create_pkgbuild_scripts',
...@@ -471,12 +513,14 @@ framework dir is 'App Product.app/Contents/Frameworks/Product Framework.framewor ...@@ -471,12 +513,14 @@ framework dir is 'App Product.app/Contents/Frameworks/Product Framework.framewor
pkgbuild_args = run_commands[0][1][0] pkgbuild_args = run_commands[0][1][0]
productbuild_args = run_commands[1][1][0] productbuild_args = run_commands[1][1][0]
self.assertEqual('$W/payload',
_get_adjacent_item(pkgbuild_args, '--root'))
self.assertEqual('$W/App Product.plist',
_get_adjacent_item(pkgbuild_args, '--component-plist'))
self.assertEqual('test.signing.bundle_id', self.assertEqual('test.signing.bundle_id',
_get_adjacent_item(pkgbuild_args, '--identifier')) _get_adjacent_item(pkgbuild_args, '--identifier'))
self.assertEqual('99.0.9999.99', self.assertEqual('99.0.9999.99',
_get_adjacent_item(pkgbuild_args, '--version')) _get_adjacent_item(pkgbuild_args, '--version'))
self.assertEqual('$W/App Product.app',
_get_adjacent_item(pkgbuild_args, '--component'))
self.assertEqual('$W/scripts', self.assertEqual('$W/scripts',
_get_adjacent_item(pkgbuild_args, '--scripts')) _get_adjacent_item(pkgbuild_args, '--scripts'))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment