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:
"""
import os.path
import plistlib
from . import commands, model, modification, notarize, parts, signing
......@@ -143,6 +144,33 @@ def _create_pkgbuild_scripts(paths, dist_config):
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):
"""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
......@@ -225,6 +253,15 @@ def _package_and_sign_pkg(paths, dist_config):
## 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
# 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.
......@@ -232,14 +269,14 @@ def _package_and_sign_pkg(paths, dist_config):
component_pkg_name = '{}.pkg'.format(dist_config.app_product).replace(
' ', '')
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)
commands.run_command([
'pkgbuild', '--identifier', dist_config.base_bundle_id, '--version',
dist_config.version, '--component', app_path, '--install-location',
'/Applications', '--scripts', scripts_path, component_pkg_path
'pkgbuild', '--root', root_directory, '--component-plist',
component_property_path, '--identifier', dist_config.base_bundle_id,
'--version', dist_config.version, '--install-location', '/Applications',
'--scripts', scripts_path, component_pkg_path
])
## The product archive.
......
......@@ -19,6 +19,10 @@ def _get_work_dir(*args, **kwargs):
_get_work_dir.count = 0
def _component_property_path(paths, dist_config):
return '$W/App Product.plist'
def _productbuild_distribution_path(p, d, c):
return '$W/App Product.dist'
......@@ -31,6 +35,17 @@ def _read_plist(p):
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):
if p == '$I/Product Packaging/pkg_postinstall.in':
return """app dir is '@APP_DIR@'
......@@ -294,6 +309,27 @@ brand code is 'MOO'
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)
def test_productbuild_distribution_path(self, **kwargs):
manager = mock.Mock()
......@@ -358,6 +394,8 @@ framework dir is 'App Product.app/Contents/Frameworks/Product Framework.framewor
self.assertEqual('AppProduct-99.0.9999.99',
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',
_productbuild_distribution_path)
@mock.patch('signing.pipeline._create_pkgbuild_scripts',
......@@ -386,12 +424,14 @@ framework dir is 'App Product.app/Contents/Frameworks/Product Framework.framewor
pkgbuild_args = run_commands[0][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',
_get_adjacent_item(pkgbuild_args, '--identifier'))
self.assertEqual('99.0.9999.99',
_get_adjacent_item(pkgbuild_args, '--version'))
self.assertEqual('$W/App Product.app',
_get_adjacent_item(pkgbuild_args, '--component'))
self.assertEqual('$W/scripts',
_get_adjacent_item(pkgbuild_args, '--scripts'))
......@@ -439,6 +479,8 @@ framework dir is 'App Product.app/Contents/Frameworks/Product Framework.framewor
self.assertEqual('AppProduct-99.0.9999.99-MOO',
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',
_productbuild_distribution_path)
@mock.patch('signing.pipeline._create_pkgbuild_scripts',
......@@ -471,12 +513,14 @@ framework dir is 'App Product.app/Contents/Frameworks/Product Framework.framewor
pkgbuild_args = run_commands[0][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',
_get_adjacent_item(pkgbuild_args, '--identifier'))
self.assertEqual('99.0.9999.99',
_get_adjacent_item(pkgbuild_args, '--version'))
self.assertEqual('$W/App Product.app',
_get_adjacent_item(pkgbuild_args, '--component'))
self.assertEqual('$W/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