changeset 44750:a39984091380 stable

packaging: integrate signing into run_wix_packaging() Previously, signing was implemented via a separate function which called build_installer() and then called signing functionality. In this model, in order to implement an alternative build mechanism, we would have to invent a new variant to handle signing as well. This commit merges the signing logic into the function invoking wix. If we pass an argument holding metadata about how to sign, we sign hg.exe and the installer. This means all we have to do is pass in signing info and the signing just works. A slight change here is that signing of hg.exe happens in the staging directory as opposed to before the staging directory is populated. I don't think this matters. Differential Revision: https://phab.mercurial-scm.org/D8475
author Gregory Szorc <gregory.szorc@gmail.com>
date Mon, 20 Apr 2020 17:53:20 -0700
parents 92627c42e7c2
children a5740490eb5f
files contrib/packaging/hgpackaging/cli.py contrib/packaging/hgpackaging/wix.py
diffstat 2 files changed, 38 insertions(+), 88 deletions(-) [+]
line wrap: on
line diff
--- a/contrib/packaging/hgpackaging/cli.py	Mon Apr 20 17:33:41 2020 -0700
+++ b/contrib/packaging/hgpackaging/cli.py	Mon Apr 20 17:53:20 2020 -0700
@@ -60,7 +60,6 @@
     extra_wxs=None,
     extra_features=None,
 ):
-    fn = wix.build_installer
     kwargs = {
         "source_dir": SOURCE_DIR,
         "python_exe": pathlib.Path(python),
@@ -80,14 +79,15 @@
         kwargs["extra_features"] = extra_features.split(",")
 
     if sign_sn or sign_cert:
-        fn = wix.build_signed_installer
-        kwargs["name"] = name
-        kwargs["subject_name"] = sign_sn
-        kwargs["cert_path"] = sign_cert
-        kwargs["cert_password"] = sign_password
-        kwargs["timestamp_url"] = sign_timestamp_url
+        kwargs["signing_info"] = {
+            "name": name,
+            "subject_name": sign_sn,
+            "cert_path": sign_cert,
+            "cert_password": sign_password,
+            "timestamp_url": sign_timestamp_url,
+        }
 
-    fn(**kwargs)
+    wix.build_installer(**kwargs)
 
 
 def get_parser():
--- a/contrib/packaging/hgpackaging/wix.py	Mon Apr 20 17:33:41 2020 -0700
+++ b/contrib/packaging/hgpackaging/wix.py	Mon Apr 20 17:53:20 2020 -0700
@@ -121,30 +121,6 @@
     subprocess.run(args, cwd=str(cwd), check=True)
 
 
-def make_post_build_signing_fn(
-    name,
-    subject_name=None,
-    cert_path=None,
-    cert_password=None,
-    timestamp_url=None,
-):
-    """Create a callable that will use signtool to sign hg.exe."""
-
-    def post_build_sign(source_dir, build_dir, dist_dir, version):
-        description = '%s %s' % (name, version)
-
-        sign_with_signtool(
-            dist_dir / 'hg.exe',
-            description,
-            subject_name=subject_name,
-            cert_path=cert_path,
-            cert_password=cert_password,
-            timestamp_url=timestamp_url,
-        )
-
-    return post_build_sign
-
-
 def make_files_xml(staging_dir: pathlib.Path, is_x64) -> str:
     """Create XML string listing every file to be installed."""
 
@@ -313,10 +289,10 @@
     python_exe: pathlib.Path,
     msi_name='mercurial',
     version=None,
-    post_build_fn=None,
     extra_packages_script=None,
     extra_wxs: typing.Optional[typing.Dict[str, str]] = None,
     extra_features: typing.Optional[typing.List[str]] = None,
+    signing_info: typing.Optional[typing.Dict[str, str]] = None,
 ):
     """Build a WiX MSI installer.
 
@@ -325,10 +301,6 @@
     ``python_exe`` is the path to the Python executable to use/bundle.
     ``version`` is the Mercurial version string. If not defined,
     ``mercurial/__version__.py`` will be consulted.
-    ``post_build_fn`` is a callable that will be called after building
-    Mercurial but before invoking WiX. It can be used to e.g. facilitate
-    signing. It is passed the paths to the Mercurial source, build, and
-    dist directories and the resolved Mercurial version.
     ``extra_packages_script`` is a command to be run to inject extra packages
     into the py2exe binary. It should stage packages into the virtualenv and
     print a null byte followed by a newline-separated list of packages that
@@ -340,7 +312,6 @@
     arch = 'x64' if r'\x64' in os.environ.get('LIB', '') else 'x86'
 
     hg_build_dir = source_dir / 'build'
-    dist_dir = source_dir / 'dist'
 
     requirements_txt = (
         source_dir / 'contrib' / 'packaging' / 'requirements_win32.txt'
@@ -362,9 +333,6 @@
     if version != orig_version:
         print('(normalized from: %s)' % orig_version)
 
-    if post_build_fn:
-        post_build_fn(source_dir, hg_build_dir, dist_dir, version)
-
     build_dir = hg_build_dir / ('wix-%s' % arch)
     staging_dir = build_dir / 'stage'
 
@@ -397,6 +365,7 @@
         msi_name=msi_name,
         extra_wxs=extra_wxs,
         extra_features=extra_features,
+        signing_info=signing_info,
     )
 
 
@@ -410,8 +379,25 @@
     msi_name: typing.Optional[str] = "mercurial",
     extra_wxs: typing.Optional[typing.Dict[str, str]] = None,
     extra_features: typing.Optional[typing.List[str]] = None,
+    signing_info: typing.Optional[typing.Dict[str, str]] = None,
 ):
-    """Invokes WiX to package up a built Mercurial."""
+    """Invokes WiX to package up a built Mercurial.
+
+    ``signing_info`` is a dict defining properties to facilitate signing the
+    installer. Recognized keys include ``name``, ``subject_name``,
+    ``cert_path``, ``cert_password``, and ``timestamp_url``. If populated,
+    we will sign both the hg.exe and the .msi using the signing credentials
+    specified.
+    """
+    if signing_info:
+        sign_with_signtool(
+            staging_dir / "hg.exe",
+            "%s %s" % (signing_info["name"], version),
+            subject_name=signing_info["subject_name"],
+            cert_path=signing_info["cert_path"],
+            cert_password=signing_info["cert_password"],
+            timestamp_url=signing_info["timestamp_url"],
+        )
 
     wix_dir = source_dir / 'contrib' / 'packaging' / 'wix'
 
@@ -475,52 +461,16 @@
 
     print('%s created' % msi_path)
 
+    if signing_info:
+        sign_with_signtool(
+            msi_path,
+            "%s %s" % (signing_info["name"], version),
+            subject_name=signing_info["subject_name"],
+            cert_path=signing_info["cert_path"],
+            cert_password=signing_info["cert_password"],
+            timestamp_url=signing_info["timestamp_url"],
+        )
+
     return {
         'msi_path': msi_path,
     }
-
-
-def build_signed_installer(
-    source_dir: pathlib.Path,
-    python_exe: pathlib.Path,
-    name: str,
-    version=None,
-    subject_name=None,
-    cert_path=None,
-    cert_password=None,
-    timestamp_url=None,
-    extra_packages_script=None,
-    extra_wxs=None,
-    extra_features=None,
-):
-    """Build an installer with signed executables."""
-
-    post_build_fn = make_post_build_signing_fn(
-        name,
-        subject_name=subject_name,
-        cert_path=cert_path,
-        cert_password=cert_password,
-        timestamp_url=timestamp_url,
-    )
-
-    info = build_installer(
-        source_dir,
-        python_exe=python_exe,
-        msi_name=name.lower(),
-        version=version,
-        post_build_fn=post_build_fn,
-        extra_packages_script=extra_packages_script,
-        extra_wxs=extra_wxs,
-        extra_features=extra_features,
-    )
-
-    description = '%s %s' % (name, version)
-
-    sign_with_signtool(
-        info['msi_path'],
-        description,
-        subject_name=subject_name,
-        cert_path=cert_path,
-        cert_password=cert_password,
-        timestamp_url=timestamp_url,
-    )