From a22d8443e826dff5dbf4720f017e7ef73bb16acb Mon Sep 17 00:00:00 2001 From: JKarasti Date: Thu, 12 Sep 2024 18:16:23 +0300 Subject: [PATCH 01/23] Fix: SyntaxWarning while generating Dangerzone.wxs --- install/windows/build-wxs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index 14aa92d..ab5397d 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -82,7 +82,7 @@ def build_dir_xml(root, data): component_el, "RegistryValue", Root="HKCU", - Key="Software\Freedom of the Press Foundation\Dangerzone", + Key="Software\\Freedom of the Press Foundation\\Dangerzone", Name="installed", Type="integer", Value="1", From 99f231b46a55ed0381a70a1c6289c49378b17996 Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 20:07:52 +0300 Subject: [PATCH 02/23] Fix: Make generated WiX authoring pass WixCop checks WixCop.exe is a built in formatting tool that comes with WiX toolset v3. This fixes `wix convert` command not beins able to run --- install/windows/build-wxs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index ab5397d..434badc 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -234,10 +234,10 @@ def main(): ET.SubElement(feature_el, "ComponentRef", Id=component_id) ET.SubElement(feature_el, "ComponentRef", Id="ApplicationShortcuts") - print('') + print('') print(f'') print('') - ET.indent(root_el) + ET.indent(root_el, space=" ") print(ET.tostring(root_el).decode()) From 6e2659bc0e3f6ed796ea2d97dded9bee892f9d45 Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 20:16:35 +0300 Subject: [PATCH 03/23] Change: Stop generating an XML declaration at the top of the WiX authoring It's not needed anymore. --- install/windows/build-wxs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index 434badc..5bd4465 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -234,7 +234,6 @@ def main(): ET.SubElement(feature_el, "ComponentRef", Id=component_id) ET.SubElement(feature_el, "ComponentRef", Id="ApplicationShortcuts") - print('') print(f'') print('') ET.indent(root_el, space=" ") From b2c085c812a46effd46fb25f83156a61d336b55d Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 20:18:58 +0300 Subject: [PATCH 04/23] Change: Update WiX schema namespace Also rename `root_el` to `wix_el`. WiX version 5 uses the same namespace. --- install/windows/build-wxs.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index 5bd4465..c96c6c5 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -158,9 +158,10 @@ def main(): ) ) - root_el = ET.Element("Wix", xmlns="http://schemas.microsoft.com/wix/2006/wi") + # Add the Wix root element + wix_el = ET.Element("Wix", xmlns="http://wixtoolset.org/schemas/v4/wxs") product_el = ET.SubElement( - root_el, + wix_el, "Product", Name="Dangerzone", Manufacturer="Freedom of the Press Foundation", @@ -236,8 +237,8 @@ def main(): print(f'') print('') - ET.indent(root_el, space=" ") - print(ET.tostring(root_el).decode()) + ET.indent(wix_el, space=" ") + print(ET.tostring(wix_el).decode()) if __name__ == "__main__": From 3357b30edb3f0991195f8c140a1a39f3f1db1b75 Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 20:21:48 +0300 Subject: [PATCH 05/23] Change: Rename `INSTALLDIR` to `INSTALLFOLDER` It's the new default name for it --- install/windows/build-wxs.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index c96c6c5..49cc83a 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -17,7 +17,7 @@ def build_data(dirname, dir_prefix, id_, name): if os.path.isfile(filename): data["files"].append(os.path.join(dir_prefix, basename)) elif os.path.isdir(filename): - if id_ == "INSTALLDIR": + if id_ == "INSTALLFOLDER": id_prefix = "Folder" else: id_prefix = id_ @@ -42,7 +42,7 @@ def build_data(dirname, dir_prefix, id_, name): ) if len(data["files"]) > 0: - if id_ == "INSTALLDIR": + if id_ == "INSTALLFOLDER": data["component_id"] = "ApplicationFiles" else: data["component_id"] = "FolderComponent" + id_[len("Folder") :] @@ -75,8 +75,8 @@ def build_dir_xml(root, data): Id="ApplicationShortcut1", Name="Dangerzone", Description="Dangerzone", - Target="[INSTALLDIR]dangerzone.exe", - WorkingDirectory="INSTALLDIR", + Target="[INSTALLFOLDER]dangerzone.exe", + WorkingDirectory="INSTALLFOLDER", ) ET.SubElement( component_el, @@ -153,7 +153,7 @@ def main(): build_data( dist_dir, "exe.win-amd64-3.12", - "INSTALLDIR", + "INSTALLFOLDER", "Dangerzone", ) ) @@ -204,7 +204,7 @@ def main(): product_el, "Property", Id="WIXUI_INSTALLDIR", - Value="INSTALLDIR", + Value="INSTALLFOLDER", ) ET.SubElement(product_el, "UIRef", Id="WixUI_InstallDir") ET.SubElement(product_el, "UIRef", Id="WixUI_ErrorProgressText") From 3df5c0d3be5fffc147dbea9d98310bbf80d76cc9 Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 20:31:58 +0300 Subject: [PATCH 06/23] Change: Merge Product into Package element - The Keywords and Description items move under a new SummaryInformation element. - Shuffle things around so that elements previously under the product element are now under the Package element. - Rename SummaryCodepage in SummaryInformation to Codepage and remove a duplicate Manufacturer item. - Remove InstallerVersion and let WiX set it to default value. (500 a.k.a Windows 7) --- install/windows/build-wxs.py | 48 +++++++++++++++++------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index 49cc83a..b494009 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -160,77 +160,75 @@ def main(): # Add the Wix root element wix_el = ET.Element("Wix", xmlns="http://wixtoolset.org/schemas/v4/wxs") - product_el = ET.SubElement( + + # Add the Package element + package_el = ET.SubElement( wix_el, - "Product", + "Package", Name="Dangerzone", Manufacturer="Freedom of the Press Foundation", - Id="*", UpgradeCode="$(var.ProductUpgradeCode)", Language="1033", + Compressed="yes", Codepage="1252", Version="$(var.ProductVersion)", ) ET.SubElement( - product_el, - "Package", - Id="*", + package_el, + "SummaryInformation", Keywords="Installer", Description="Dangerzone $(var.ProductVersion) Installer", - Manufacturer="Freedom of the Press Foundation", - InstallerVersion="100", - Languages="1033", - Compressed="yes", - SummaryCodepage="1252", + Codepage="1252", ) - ET.SubElement(product_el, "Media", Id="1", Cabinet="product.cab", EmbedCab="yes") + ET.SubElement(package_el, "Media", Id="1", Cabinet="product.cab", EmbedCab="yes") ET.SubElement( - product_el, "Icon", Id="ProductIcon", SourceFile="..\\share\\dangerzone.ico" + package_el, "Icon", Id="ProductIcon", SourceFile="..\\share\\dangerzone.ico" ) - ET.SubElement(product_el, "Property", Id="ARPPRODUCTICON", Value="ProductIcon") + ET.SubElement(package_el, "Property", Id="ARPPRODUCTICON", Value="ProductIcon") ET.SubElement( - product_el, + package_el, "Property", Id="ARPHELPLINK", Value="https://dangerzone.rocks", ) ET.SubElement( - product_el, + package_el, "Property", Id="ARPURLINFOABOUT", Value="https://freedom.press", ) ET.SubElement( - product_el, + package_el, "Property", Id="WIXUI_INSTALLDIR", Value="INSTALLFOLDER", ) - ET.SubElement(product_el, "UIRef", Id="WixUI_InstallDir") - ET.SubElement(product_el, "UIRef", Id="WixUI_ErrorProgressText") + ET.SubElement(package_el, "UIRef", Id="WixUI_InstallDir") + ET.SubElement(package_el, "UIRef", Id="WixUI_ErrorProgressText") ET.SubElement( - product_el, + package_el, "WixVariable", Id="WixUILicenseRtf", Value="..\\install\\windows\\license.rtf", ) ET.SubElement( - product_el, + package_el, "WixVariable", Id="WixUIDialogBmp", Value="..\\install\\windows\\dialog.bmp", ) ET.SubElement( - product_el, + package_el, "MajorUpgrade", AllowSameVersionUpgrades="yes", DowngradeErrorMessage="A newer version of [ProductName] is already installed. If you are sure you want to downgrade, remove the existing installation via Programs and Features.", ) - build_dir_xml(product_el, data) - component_ids = build_components_xml(product_el, data) + build_dir_xml(package_el, data) + component_ids = build_components_xml(package_el, data) - feature_el = ET.SubElement(product_el, "Feature", Id="DefaultFeature", Level="1") + # Add the Feature element + feature_el = ET.SubElement(package_el, "Feature", Id="DefaultFeature", Level="1") for component_id in component_ids: ET.SubElement(feature_el, "ComponentRef", Id=component_id) ET.SubElement(feature_el, "ComponentRef", Id="ApplicationShortcuts") From 9a87e64e69898764eaea3ff0722be0e40e65834a Mon Sep 17 00:00:00 2001 From: jkarasti Date: Mon, 11 Nov 2024 19:06:10 +0200 Subject: [PATCH 07/23] Change: Disable `AllowSameVersionUpgrades` Since running `wix msi validate` with it set to `yes` causes an error. --- install/windows/build-wxs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index b494009..7a0d8f1 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -220,7 +220,6 @@ def main(): ET.SubElement( package_el, "MajorUpgrade", - AllowSameVersionUpgrades="yes", DowngradeErrorMessage="A newer version of [ProductName] is already installed. If you are sure you want to downgrade, remove the existing installation via Programs and Features.", ) From 28b555ac6f06d2e27ad97e6cd8f290da58098cd5 Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 20:43:31 +0300 Subject: [PATCH 08/23] Change: Wrap `ProgramMenuFolder` component with a `StandardDirectory` component --- install/windows/build-wxs.py | 65 ++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 32 deletions(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index 7a0d8f1..08396de 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -61,34 +61,6 @@ def build_dir_xml(root, data): for subdata in data["dirs"]: build_dir_xml(el, subdata) - # If this is the ProgramMenuFolder, add the menu component - if "id" in data and data["id"] == "ProgramMenuFolder": - component_el = ET.SubElement( - el, - "Component", - Id="ApplicationShortcuts", - Guid="539e7de8-a124-4c09-aa55-0dd516aad7bc", - ) - ET.SubElement( - component_el, - "Shortcut", - Id="ApplicationShortcut1", - Name="Dangerzone", - Description="Dangerzone", - Target="[INSTALLFOLDER]dangerzone.exe", - WorkingDirectory="INSTALLFOLDER", - ) - ET.SubElement( - component_el, - "RegistryValue", - Root="HKCU", - Key="Software\\Freedom of the Press Foundation\\Dangerzone", - Name="installed", - Type="integer", - Value="1", - KeyPath="yes", - ) - def build_components_xml(root, data): component_ids = [] @@ -142,10 +114,6 @@ def main(): "id": "ProgramFilesFolder", "dirs": [], }, - { - "id": "ProgramMenuFolder", - "dirs": [], - }, ], } @@ -223,6 +191,39 @@ def main(): DowngradeErrorMessage="A newer version of [ProductName] is already installed. If you are sure you want to downgrade, remove the existing installation via Programs and Features.", ) + # Add the ProgramMenuFolder StandardDirectory + programmenufolder_el = ET.SubElement( + package_el, + "StandardDirectory", + Id="ProgramMenuFolder", + ) + # Add a shortcut for Dangerzone in the Start menu + shortcut_el = ET.SubElement( + programmenufolder_el, + "Component", + Id="ApplicationShortcuts", + Guid="539e7de8-a124-4c09-aa55-0dd516aad7bc", + ) + ET.SubElement( + shortcut_el, + "Shortcut", + Id="DangerzoneStartMenuShortcut", + Name="Dangerzone", + Description="Dangerzone", + Target="[INSTALLFOLDER]dangerzone.exe", + WorkingDirectory="INSTALLFOLDER", + ) + ET.SubElement( + shortcut_el, + "RegistryValue", + Root="HKCU", + Key="Software\\Freedom of the Press Foundation\\Dangerzone", + Name="installed", + Type="integer", + Value="1", + KeyPath="yes", + ) + build_dir_xml(package_el, data) component_ids = build_components_xml(package_el, data) From e6f5a258189bff545375957e64bee2865b06b6a2 Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 21:17:43 +0300 Subject: [PATCH 09/23] Change: Wrap `ProgramFilesFolder` component with a `StandardDirectory` component --- install/windows/build-wxs.py | 55 ++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index 08396de..6b94291 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -67,20 +67,19 @@ def build_components_xml(root, data): if "component_id" in data: component_ids.append(data["component_id"]) - for subdata in data["dirs"]: - if "component_guid" in subdata: - dir_ref_el = ET.SubElement(root, "DirectoryRef", Id=subdata["id"]) + if "component_guid" in data: + dir_ref_el = ET.SubElement(root, "DirectoryRef", Id=data["id"]) component_el = ET.SubElement( dir_ref_el, "Component", - Id=subdata["component_id"], - Guid=subdata["component_guid"], + Id=data["component_id"], + Guid=data["component_guid"], ) - for filename in subdata["files"]: + for filename in data["files"]: file_el = ET.SubElement( component_el, "File", Source=filename, Id="file_" + uuid.uuid4().hex ) - + for subdata in data["dirs"]: component_ids += build_components_xml(root, subdata) return component_ids @@ -106,24 +105,12 @@ def main(): print("You must build the dangerzone binary before running this") return - data = { - "id": "TARGETDIR", - "name": "SourceDir", - "dirs": [ - { - "id": "ProgramFilesFolder", - "dirs": [], - }, - ], - } - - data["dirs"][0]["dirs"].append( - build_data( - dist_dir, - "exe.win-amd64-3.12", - "INSTALLFOLDER", - "Dangerzone", - ) + # Prepare data for WiX file harvesting from the output of cx_Freeze + data = build_data( + dist_dir, + "exe.win-amd64-3.12", + "INSTALLFOLDER", + "Dangerzone", ) # Add the Wix root element @@ -224,8 +211,22 @@ def main(): KeyPath="yes", ) - build_dir_xml(package_el, data) - component_ids = build_components_xml(package_el, data) + # Add the ProgramFilesFolder StandardDirectory + programfilesfolder_el = ET.SubElement( + package_el, + "StandardDirectory", + Id="ProgramFilesFolder", + ) + + # Create the directory structure for the installed product + build_dir_xml(programfilesfolder_el, data) + + # Create a component group for application components + applicationcomponents_el = ET.SubElement( + package_el, "ComponentGroup", Id="ApplicationComponents" + ) + # Populate the application components group with components for the installed package + build_components_xml(applicationcomponents_el, data) # Add the Feature element feature_el = ET.SubElement(package_el, "Feature", Id="DefaultFeature", Level="1") From 68cd96a7d35dd6b48a34ba21e66a58755016d29d Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 21:21:00 +0300 Subject: [PATCH 10/23] Change: Convert Wix UI extension authoring to WiX Toolset v5 Due to limitations of the xml.etree.ElementTree library, add the items in the root element as a dictionary --- install/windows/build-wxs.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index 6b94291..fdeac9b 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -114,7 +114,13 @@ def main(): ) # Add the Wix root element - wix_el = ET.Element("Wix", xmlns="http://wixtoolset.org/schemas/v4/wxs") + wix_el = ET.Element( + "Wix", + { + "xmlns": "http://wixtoolset.org/schemas/v4/wxs", + "xmlns:ui": "http://wixtoolset.org/schemas/v4/wxs/ui", + }, + ) # Add the Package element package_el = ET.SubElement( @@ -153,12 +159,8 @@ def main(): Value="https://freedom.press", ) ET.SubElement( - package_el, - "Property", - Id="WIXUI_INSTALLDIR", - Value="INSTALLFOLDER", + package_el, "ui:WixUI", Id="WixUI_InstallDir", InstallDirectory="INSTALLFOLDER" ) - ET.SubElement(package_el, "UIRef", Id="WixUI_InstallDir") ET.SubElement(package_el, "UIRef", Id="WixUI_ErrorProgressText") ET.SubElement( package_el, From 410beead11e4f93fe6b3f8e633a018b8598b54d2 Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 21:23:20 +0300 Subject: [PATCH 11/23] Change: Swap Media element with MediaTemplate This is a new default and makes authoring slightly simpler without any functional changes. --- install/windows/build-wxs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index fdeac9b..2849591 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -141,7 +141,7 @@ def main(): Description="Dangerzone $(var.ProductVersion) Installer", Codepage="1252", ) - ET.SubElement(package_el, "Media", Id="1", Cabinet="product.cab", EmbedCab="yes") + ET.SubElement(package_el, "MediaTemplate", EmbedCab="yes") ET.SubElement( package_el, "Icon", Id="ProductIcon", SourceFile="..\\share\\dangerzone.ico" ) From c7e3f995c03417be3fb77c3efee32a1c62cb8c5f Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 21:34:05 +0300 Subject: [PATCH 12/23] Refactor: Simplify `build_data()` function - Rename variables to be more clear about what they do: - reorganise code - simplify a few checks --- install/windows/build-wxs.py | 61 ++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index 2849591..55d799f 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -4,49 +4,50 @@ import uuid import xml.etree.ElementTree as ET -def build_data(dirname, dir_prefix, id_, name): +def build_data(base_path, path_prefix, dir_id, dir_name): data = { - "id": id_, - "name": name, + "directory_name": dir_name, + "directory_id": dir_id, "files": [], "dirs": [], } - for basename in os.listdir(dirname): - filename = os.path.join(dirname, basename) - if os.path.isfile(filename): - data["files"].append(os.path.join(dir_prefix, basename)) - elif os.path.isdir(filename): - if id_ == "INSTALLFOLDER": - id_prefix = "Folder" + if dir_id == "INSTALLFOLDER": + data["component_id"] = "ApplicationFiles" + else: + data["component_id"] = "Component" + dir_id + data["component_guid"] = str(uuid.uuid4()) + + for entry in os.listdir(base_path): + entry_path = os.path.join(base_path, entry) + if os.path.isfile(entry_path): + data["files"].append(os.path.join(path_prefix, entry)) + elif os.path.isdir(entry_path): + if dir_id == "INSTALLFOLDER": + next_dir_prefix = "Folder" else: - id_prefix = id_ + next_dir_prefix = dir_id # Skip lib/PySide6/examples folder due to ilegal file names - if "\\build\\exe.win-amd64-3.12\\lib\\PySide6\\examples" in dirname: + if "\\build\\exe.win-amd64-3.12\\lib\\PySide6\\examples" in base_path: continue # Skip lib/PySide6/qml/QtQuick folder due to ilegal file names # XXX Since we're not using Qml it should be no problem - if "\\build\\exe.win-amd64-3.12\\lib\\PySide6\\qml\\QtQuick" in dirname: + if "\\build\\exe.win-amd64-3.12\\lib\\PySide6\\qml\\QtQuick" in base_path: continue - id_value = f"{id_prefix}{basename.capitalize().replace('-', '_')}" - data["dirs"].append( - build_data( - os.path.join(dirname, basename), - os.path.join(dir_prefix, basename), - id_value, - basename, - ) + next_dir_id = next_dir_prefix + entry.capitalize().replace("-", "_") + subdata = build_data( + os.path.join(base_path, entry), + os.path.join(path_prefix, entry), + next_dir_id, + entry, ) - if len(data["files"]) > 0: - if id_ == "INSTALLFOLDER": - data["component_id"] = "ApplicationFiles" - else: - data["component_id"] = "FolderComponent" + id_[len("Folder") :] - data["component_guid"] = str(uuid.uuid4()) + # Add the subdirectory only if it contains files or subdirectories + if subdata["files"] or subdata["dirs"]: + data["dirs"].append(subdata) return data @@ -54,9 +55,9 @@ def build_data(dirname, dir_prefix, id_, name): def build_dir_xml(root, data): attrs = {} if "id" in data: - attrs["Id"] = data["id"] + attrs["Id"] = data["directory_id"] if "name" in data: - attrs["Name"] = data["name"] + attrs["Name"] = data["directory_name"] el = ET.SubElement(root, "Directory", attrs) for subdata in data["dirs"]: build_dir_xml(el, subdata) @@ -68,7 +69,7 @@ def build_components_xml(root, data): component_ids.append(data["component_id"]) if "component_guid" in data: - dir_ref_el = ET.SubElement(root, "DirectoryRef", Id=data["id"]) + dir_ref_el = ET.SubElement(root, "DirectoryRef", Id=data["directory_id"]) component_el = ET.SubElement( dir_ref_el, "Component", From 2b73427e1650ade015f83e02fa81b372c59600f9 Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 21:57:38 +0300 Subject: [PATCH 13/23] Change: Wrap all files to be included in the .msi in a `ComponentGroupRef` With this, all the files are organised into Components, each of which points to a Directory defined in the StandardDirectory element. This simplifies the Feature element considerable as only thing it needs to include everything in the built msi is a reference to `ApplicationComponents` --- install/windows/build-wxs.py | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index 55d799f..68dd6d4 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -64,26 +64,17 @@ def build_dir_xml(root, data): def build_components_xml(root, data): - component_ids = [] - if "component_id" in data: - component_ids.append(data["component_id"]) - - if "component_guid" in data: - dir_ref_el = ET.SubElement(root, "DirectoryRef", Id=data["directory_id"]) - component_el = ET.SubElement( - dir_ref_el, - "Component", - Id=data["component_id"], - Guid=data["component_guid"], - ) - for filename in data["files"]: - file_el = ET.SubElement( - component_el, "File", Source=filename, Id="file_" + uuid.uuid4().hex - ) + component_el = ET.SubElement( + root, + "Component", + Id=data["component_id"], + Guid=data["component_guid"], + Directory=data["directory_id"], + ) + for filename in data["files"]: + ET.SubElement(component_el, "File", Source=filename) for subdata in data["dirs"]: - component_ids += build_components_xml(root, subdata) - - return component_ids + build_components_xml(root, subdata) def main(): @@ -233,8 +224,7 @@ def main(): # Add the Feature element feature_el = ET.SubElement(package_el, "Feature", Id="DefaultFeature", Level="1") - for component_id in component_ids: - ET.SubElement(feature_el, "ComponentRef", Id=component_id) + ET.SubElement(feature_el, "ComponentGroupRef", Id="ApplicationComponents") ET.SubElement(feature_el, "ComponentRef", Id="ApplicationShortcuts") print(f'') From 02c8396a9aeb70b417154f792fe78e2afba6555c Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 22:12:40 +0300 Subject: [PATCH 14/23] Refactor: `build_dir_xml()` function - rename for clarity - remove unnecessary checks --- install/windows/build-wxs.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index 68dd6d4..fb81ad1 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -52,15 +52,13 @@ def build_data(base_path, path_prefix, dir_id, dir_name): return data -def build_dir_xml(root, data): +def build_directory_xml(root, data): attrs = {} - if "id" in data: - attrs["Id"] = data["directory_id"] - if "name" in data: - attrs["Name"] = data["directory_name"] - el = ET.SubElement(root, "Directory", attrs) + attrs["Id"] = data["directory_id"] + attrs["Name"] = data["directory_name"] + directory_el = ET.SubElement(root, "Directory", attrs) for subdata in data["dirs"]: - build_dir_xml(el, subdata) + build_directory_xml(directory_el, subdata) def build_components_xml(root, data): @@ -213,7 +211,7 @@ def main(): ) # Create the directory structure for the installed product - build_dir_xml(programfilesfolder_el, data) + build_directory_xml(programfilesfolder_el, data) # Create a component group for application components applicationcomponents_el = ET.SubElement( From 9da607093f9191c75ebb6fbf5b050ae899ac31ae Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 22:17:03 +0300 Subject: [PATCH 15/23] Change: Write dangerzone version and upgradecode into Package and SummaryInformation elements directly --- install/windows/build-wxs.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index fb81ad1..a1ee569 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -118,17 +118,17 @@ def main(): "Package", Name="Dangerzone", Manufacturer="Freedom of the Press Foundation", - UpgradeCode="$(var.ProductUpgradeCode)", + UpgradeCode="12b9695c-965b-4be0-bc33-21274e809576", Language="1033", Compressed="yes", Codepage="1252", - Version="$(var.ProductVersion)", + Version=version, ) ET.SubElement( package_el, "SummaryInformation", Keywords="Installer", - Description="Dangerzone $(var.ProductVersion) Installer", + Description="Dangerzone " + version + " Installer", Codepage="1252", ) ET.SubElement(package_el, "MediaTemplate", EmbedCab="yes") @@ -225,8 +225,6 @@ def main(): ET.SubElement(feature_el, "ComponentGroupRef", Id="ApplicationComponents") ET.SubElement(feature_el, "ComponentRef", Id="ApplicationShortcuts") - print(f'') - print('') ET.indent(wix_el, space=" ") print(ET.tostring(wix_el).decode()) From 39062804bfd21c284dd8ecde14e0fad515185097 Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 22:31:16 +0300 Subject: [PATCH 16/23] Fix: Make GUIDs uppercase See [1] [1] https://learn.microsoft.com/en-us/windows/win32/msi/guid --- install/windows/build-wxs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index a1ee569..7ae4028 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -16,7 +16,7 @@ def build_data(base_path, path_prefix, dir_id, dir_name): data["component_id"] = "ApplicationFiles" else: data["component_id"] = "Component" + dir_id - data["component_guid"] = str(uuid.uuid4()) + data["component_guid"] = str(uuid.uuid4()).upper() for entry in os.listdir(base_path): entry_path = os.path.join(base_path, entry) @@ -118,7 +118,7 @@ def main(): "Package", Name="Dangerzone", Manufacturer="Freedom of the Press Foundation", - UpgradeCode="12b9695c-965b-4be0-bc33-21274e809576", + UpgradeCode="12B9695C-965B-4BE0-BC33-21274E809576", Language="1033", Compressed="yes", Codepage="1252", @@ -181,7 +181,7 @@ def main(): programmenufolder_el, "Component", Id="ApplicationShortcuts", - Guid="539e7de8-a124-4c09-aa55-0dd516aad7bc", + Guid="539E7DE8-A124-4C09-AA55-0DD516AAD7BC", ) ET.SubElement( shortcut_el, From ada9f47d655746efa79b25868a01732fb633202a Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 22:35:34 +0300 Subject: [PATCH 17/23] Change: Write Dangerzone.wxs inside the script directly Also reduce duplication slightly by definig `build_dir`, `cx_freeze_dir` and `dist_dir` --- install/windows/build-wxs.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index 7ae4028..7a15cdb 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -86,11 +86,15 @@ def main(): # -rc markers. version = f.read().strip().split("-")[0] - dist_dir = os.path.join( + build_dir = os.path.join( os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "build", - "exe.win-amd64-3.12", ) + + cx_freeze_dir = "exe.win-amd64-3.12" + + dist_dir = os.path.join(build_dir, cx_freeze_dir) + if not os.path.exists(dist_dir): print("You must build the dangerzone binary before running this") return @@ -98,7 +102,7 @@ def main(): # Prepare data for WiX file harvesting from the output of cx_Freeze data = build_data( dist_dir, - "exe.win-amd64-3.12", + cx_freeze_dir, "INSTALLFOLDER", "Dangerzone", ) @@ -226,7 +230,9 @@ def main(): ET.SubElement(feature_el, "ComponentRef", Id="ApplicationShortcuts") ET.indent(wix_el, space=" ") - print(ET.tostring(wix_el).decode()) + + with open(os.path.join(build_dir, "Dangerzone.wxs"), "w") as wxs_file: + wxs_file.write(ET.tostring(wix_el).decode()) if __name__ == "__main__": From 0767e0105327e05b92b3814912766912862491d6 Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 22:38:47 +0300 Subject: [PATCH 18/23] CI: Use WiX Toolset v5 to build the msi --- .github/workflows/ci.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ca94580..20d63df 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -110,10 +110,14 @@ jobs: key: v1-tessdata-${{ hashFiles('./install/common/download-tessdata.py') }} - name: Run CLI tests run: poetry run make test - # Taken from: https://github.com/orgs/community/discussions/27149#discussioncomment-3254829 - - name: Set path for candle and light - run: echo "C:\Program Files (x86)\WiX Toolset v3.14\bin" >> $GITHUB_PATH - shell: bash + - name: Set up .NET CLI environment + uses: actions/setup-dotnet@v4 + with: + dotnet-version: "8.x" + - name: Install WiX Toolset + run: dotnet tool install --global wix + - name: Add WiX UI extension + run: wix extension add --global WixToolset.UI.wixext - name: Build the MSI installer # NOTE: This also builds the .exe internally. run: poetry run .\install\windows\build-app.bat From 63dbe6a037e6b8762c1e3b910c1fbef15f35f479 Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 22:44:46 +0300 Subject: [PATCH 19/23] Change: Update the `build-app.bat` script to work with WiX Toolset v5 - WiX Toolset v3 used to validate the msi package by default. In v5 that has moved to a new command, so add a new validation step to the script. - Also emove the step that uses `insignia.exe` to sign the Dangerzone.msi with the digital signatures from its external cab archives. In WiX Toolset v4 and newer, insignia is replaced with a new command `wix msi inscribe`, but we tell wix to embed the cabinets into the .msi (That's what`EmbedCab="yes"` in the Media / MediaTemplate element does) so singning them separately is not necessary. [0] [0] https://wixtoolset.org/docs/tools/signing/ --- install/windows/build-app.bat | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/install/windows/build-app.bat b/install/windows/build-app.bat index 1d2b770..44c8e81 100644 --- a/install/windows/build-app.bat +++ b/install/windows/build-app.bat @@ -17,22 +17,23 @@ signtool.exe sign /v /d "Dangerzone" /a /n "Freedom of the Press Foundation" /fd REM verify the signature of dangerzone-cli.exe signtool.exe verify /pa build\exe.win-amd64-3.12\dangerzone-cli.exe -REM build the wix file -python install\windows\build-wxs.py > build\Dangerzone.wxs +REM build the wxs file +python install\windows\build-wxs.py REM build the msi package cd build -candle.exe Dangerzone.wxs -light.exe -ext WixUIExtension Dangerzone.wixobj +wix build -ext WixToolset.UI.wixext .\Dangerzone.wxs -out Dangerzone.msi + +REM validate Dangerzone.msi +wix msi validate Dangerzone.msi REM code sign Dangerzone.msi -insignia.exe -im Dangerzone.msi signtool.exe sign /v /d "Dangerzone" /a /n "Freedom of the Press Foundation" /fd sha256 /t http://time.certum.pl/ Dangerzone.msi REM verify the signature of Dangerzone.msi signtool.exe verify /pa Dangerzone.msi -REM moving Dangerzone.msi to dist +REM move Dangerzone.msi to dist cd .. mkdir dist move build\Dangerzone.msi dist From 3eaa89fcce9dc278fccdc6fa143a16635a059e58 Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 22:46:36 +0300 Subject: [PATCH 20/23] Docs: Documentation for WiX Toolset 5 --- BUILD.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/BUILD.md b/BUILD.md index c395f50..b727fc3 100644 --- a/BUILD.md +++ b/BUILD.md @@ -474,11 +474,22 @@ poetry shell .\dev_scripts\dangerzone.bat ``` -### If you want to build the installer +### If you want to build the Windows installer -* Go to https://dotnet.microsoft.com/download/dotnet-framework and download and install .NET Framework 3.5 SP1 Runtime. I downloaded `dotnetfx35.exe`. -* Go to https://wixtoolset.org/releases/ and download and install WiX toolset. I downloaded `wix314.exe`. -* Add `C:\Program Files (x86)\WiX Toolset v3.14\bin` to the path ([instructions](https://web.archive.org/web/20230221104142/https://windowsloop.com/how-to-add-to-windows-path/)). +Install [.NET SDK](https://dotnet.microsoft.com/en-us/download) version 6 or later. Then, open a terminal and install the [WiX Toolset .NET tool](https://wixtoolset.org/) v5.0.1. + +```sh +dotnet tool install --global wix --version 5.0.1 +``` + +Install the WiX UI extension **in a new terminal**, in order to use the newly installed `wix` .NET tool: + +```sh +wix extension add --global WixToolset.UI.wixext/5.0.1 +``` + +> [!IMPORTANT] +> To prevent compatibility issues, ensure that all WiX plugins you install match the version of WiX Toolset. ### If you want to sign binaries with Authenticode From 945f1846d1071ddba57a4d67e9535c657ca5efa2 Mon Sep 17 00:00:00 2001 From: JKarasti Date: Mon, 23 Sep 2024 23:17:02 +0300 Subject: [PATCH 21/23] Change: Build a 64-bit installer --- install/windows/build-app.bat | 2 +- install/windows/build-wxs.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/install/windows/build-app.bat b/install/windows/build-app.bat index 44c8e81..ea74429 100644 --- a/install/windows/build-app.bat +++ b/install/windows/build-app.bat @@ -22,7 +22,7 @@ python install\windows\build-wxs.py REM build the msi package cd build -wix build -ext WixToolset.UI.wixext .\Dangerzone.wxs -out Dangerzone.msi +wix build -arch x64 -ext WixToolset.UI.wixext .\Dangerzone.wxs -out Dangerzone.msi REM validate Dangerzone.msi wix msi validate Dangerzone.msi diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index 7a15cdb..a195872 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -211,7 +211,7 @@ def main(): programfilesfolder_el = ET.SubElement( package_el, "StandardDirectory", - Id="ProgramFilesFolder", + Id="ProgramFiles64Folder", ) # Create the directory structure for the installed product From a900a7c42e39aadeab6c31bf830fe2a943a842f7 Mon Sep 17 00:00:00 2001 From: jkarasti Date: Tue, 29 Oct 2024 18:56:56 +0200 Subject: [PATCH 22/23] Change: Wrap installer ui related things in a `UI` element --- install/windows/build-wxs.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index a195872..a08aaf8 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -152,10 +152,6 @@ def main(): Id="ARPURLINFOABOUT", Value="https://freedom.press", ) - ET.SubElement( - package_el, "ui:WixUI", Id="WixUI_InstallDir", InstallDirectory="INSTALLFOLDER" - ) - ET.SubElement(package_el, "UIRef", Id="WixUI_ErrorProgressText") ET.SubElement( package_el, "WixVariable", @@ -174,6 +170,13 @@ def main(): DowngradeErrorMessage="A newer version of [ProductName] is already installed. If you are sure you want to downgrade, remove the existing installation via Programs and Features.", ) + # Add The UI element + ui_el = ET.SubElement(package_el, "UI") + ET.SubElement( + ui_el, "ui:WixUI", Id="WixUI_InstallDir", InstallDirectory="INSTALLFOLDER" + ) + ET.SubElement(ui_el, "UIRef", Id="WixUI_ErrorProgressText") + # Add the ProgramMenuFolder StandardDirectory programmenufolder_el = ET.SubElement( package_el, From 8cb83ca6109c3510eb0d68d1f47ed6adf661ccdc Mon Sep 17 00:00:00 2001 From: jkarasti Date: Mon, 11 Nov 2024 18:46:22 +0200 Subject: [PATCH 23/23] Fixup: Tweak wix install doc --- BUILD.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/BUILD.md b/BUILD.md index b727fc3..f6a527e 100644 --- a/BUILD.md +++ b/BUILD.md @@ -476,20 +476,21 @@ poetry shell ### If you want to build the Windows installer -Install [.NET SDK](https://dotnet.microsoft.com/en-us/download) version 6 or later. Then, open a terminal and install the [WiX Toolset .NET tool](https://wixtoolset.org/) v5.0.1. +Install [.NET SDK](https://dotnet.microsoft.com/en-us/download) version 6 or later. Then, open a terminal and install the latest version of [WiX Toolset .NET tool](https://wixtoolset.org/) **v5** with: ```sh -dotnet tool install --global wix --version 5.0.1 +dotnet tool install --global wix --version 5.* ``` -Install the WiX UI extension **in a new terminal**, in order to use the newly installed `wix` .NET tool: +Install the WiX UI extension. You may need to open a new terminal in order to use the newly installed `wix` .NET tool: ```sh -wix extension add --global WixToolset.UI.wixext/5.0.1 +wix extension add --global WixToolset.UI.wixext/5.x.y ``` > [!IMPORTANT] -> To prevent compatibility issues, ensure that all WiX plugins you install match the version of WiX Toolset. +> To avoid compatibility issues, ensure the WiX UI extension version matches the version of the WiX Toolset. +> Run `wix --version` to check the version of WiX Toolset you have installed and replace `5.x.y` with the full version number without the Git revision. ### If you want to sign binaries with Authenticode