diff --git a/.github/workflows/build_release_assets.yml b/.github/workflows/build_release_assets.yml index f7157675c2..896ed98078 100644 --- a/.github/workflows/build_release_assets.yml +++ b/.github/workflows/build_release_assets.yml @@ -161,6 +161,11 @@ jobs: # Make it available cp rcodesign /usr/local/bin + - name: Install WiX Toolset + if: matrix.os == 'windows-2022' + shell: bash + run: dotnet tool install --global wix --version 5.0.2 + - name: Install dependencies shell: bash run: | @@ -273,6 +278,7 @@ jobs: packages/ggshield-*.rpm packages/ggshield_*.deb packages/ggshield.*.nupkg + packages/ggshield-*.msi # Run some basic tests, the goal is to verify the ggshield binary has all the # libraries it needs to run diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d296ce1e35..8ad41e47aa 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -35,6 +35,7 @@ repos: rev: v3.6.0 hooks: - id: prettier + exclude: \.wxs$ - repo: https://github.com/pycqa/isort rev: 5.13.2 diff --git a/changelog.d/20260218_145052_aurelien.gateau_msi.md b/changelog.d/20260218_145052_aurelien.gateau_msi.md new file mode 100644 index 0000000000..62643223c2 --- /dev/null +++ b/changelog.d/20260218_145052_aurelien.gateau_msi.md @@ -0,0 +1,3 @@ +### Added + +- ggshield is now available as a MSI package. diff --git a/doc/dev/os-packages.md b/doc/dev/os-packages.md index 26ec6b0adb..fde117f2af 100644 --- a/doc/dev/os-packages.md +++ b/doc/dev/os-packages.md @@ -23,6 +23,7 @@ flowchart TD signing -->|no| create_archive sign --> create_archive --> pkg[/"pkg 🍏"/] create_archive --> zip[/"zip 🪟"/] + create_archive --> msi[/"msi 🪟"/] create_archive --> tar.gz[/"tar.gz 🐧"/] create_archive --> deb[/"deb 🐧"/] create_archive --> rpm[/"rpm 🐧"/] @@ -69,6 +70,10 @@ For Gatekeeper to accept the app, the executable and all the dynamic libraries m ## Windows specific information +### Requirements + +Building the MSI requires the [.NET SDK](https://dotnet.microsoft.com/download) and the WiX CLI tool (`dotnet tool install --global wix --version 5.0.2`). + ### Signing We use [DigiCert](https://www.digicert.com) to sign `ggshield` Windows binaries. @@ -96,3 +101,13 @@ Note 2: `install-keylocker-tools` expects `$PATH` to already contain the install #### Building signed binaries Once all environment variables are set and DigiCert tools are installed, one can build signed Windows binaries using `build-os-packages --sign`. + +### MSI Package + +The build produces an MSI installer (`ggshield-VERSION-x86_64-pc-windows-msvc.msi`) using [WiX Toolset v5](https://wixtoolset.org/). The WiX source file is `scripts/build-os-packages/ggshield.wxs`. + +- **Install location**: `C:\Program Files\GitGuardian\ggshield\` +- **PATH**: The installer adds the install location to the system `PATH`. This is removed on uninstall. +- **Upgrades**: Installing a newer version automatically removes the previous one (via `MajorUpgrade`). +- **Silent install**: `msiexec /i ggshield-X.Y.Z-x86_64-pc-windows-msvc.msi /quiet` +- **Silent uninstall**: `msiexec /x ggshield-X.Y.Z-x86_64-pc-windows-msvc.msi /quiet` diff --git a/scripts/build-os-packages/build-os-packages b/scripts/build-os-packages/build-os-packages index 5508114ce1..6c326ea304 100755 --- a/scripts/build-os-packages/build-os-packages +++ b/scripts/build-os-packages/build-os-packages @@ -114,7 +114,7 @@ init_system_vars() { EXE_EXT=".exe" HUMAN_OS=Windows TARGET="$arch-pc-windows-msvc" - REQUIREMENTS="$REQUIREMENTS choco" + REQUIREMENTS="$REQUIREMENTS choco wix" ;; *) die "Unknown OS. uname printed '$out'" @@ -323,6 +323,7 @@ step_create_archive() { Windows) create_windows_packages test_chocolatey_package + test_msi_package ;; esac } diff --git a/scripts/build-os-packages/ggshield.wxs b/scripts/build-os-packages/ggshield.wxs new file mode 100644 index 0000000000..9031f0f37e --- /dev/null +++ b/scripts/build-os-packages/ggshield.wxs @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/build-os-packages/windows-functions.bash b/scripts/build-os-packages/windows-functions.bash index 44b02286ab..63ebd1cafe 100644 --- a/scripts/build-os-packages/windows-functions.bash +++ b/scripts/build-os-packages/windows-functions.bash @@ -62,7 +62,53 @@ test_chocolatey_package() { popd } +windows_build_msi_package() { + # MSI only supports X.Y.Z version format, strip any suffix (e.g. +sha) + local msi_version="${VERSION%%[+]*}" + + local wxs_path + wxs_path=$(cygpath -w "$SCRIPT_DIR/ggshield.wxs") + + local source_dir + source_dir=$(cygpath -w "$PACKAGES_DIR/$ARCHIVE_DIR_NAME") + + local msi_path="$PACKAGES_DIR/$ARCHIVE_DIR_NAME.msi" + local msi_path_win + msi_path_win=$(cygpath -w "$msi_path") + + info "Building MSI package" + wix build "$wxs_path" \ + -arch x64 \ + -d Version="$msi_version" \ + -bindpath "SourceDir=$source_dir" \ + -o "$msi_path_win" + + if [ "$DO_SIGN" -eq 1 ] ; then + info "Signing MSI package" + smctl sign \ + --verbose \ + --exit-non-zero-on-fail \ + --fingerprint "$WINDOWS_CERT_FINGERPRINT" \ + --tool signtool \ + --input "$msi_path" + fi + + info "MSI package created in $msi_path" +} + +test_msi_package() { + local msi_path="$PACKAGES_DIR/$ARCHIVE_DIR_NAME.msi" + if [ ! -f "$msi_path" ] ; then + die "MSI package not found: $msi_path" + fi + if [ ! -s "$msi_path" ] ; then + die "MSI package is empty: $msi_path" + fi + info "MSI package OK: $msi_path" +} + create_windows_packages() { windows_create_archive windows_build_chocolatey_package + windows_build_msi_package }