Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/build_release_assets.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ repos:
rev: v3.6.0
hooks:
- id: prettier
exclude: \.wxs$

- repo: https://github.com/pycqa/isort
rev: 5.13.2
Expand Down
3 changes: 3 additions & 0 deletions changelog.d/20260218_145052_aurelien.gateau_msi.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
### Added

- ggshield is now available as a MSI package.
15 changes: 15 additions & 0 deletions doc/dev/os-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 🐧"/]
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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`
3 changes: 2 additions & 1 deletion scripts/build-os-packages/build-os-packages
Original file line number Diff line number Diff line change
Expand Up @@ -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'"
Expand Down Expand Up @@ -323,6 +323,7 @@ step_create_archive() {
Windows)
create_windows_packages
test_chocolatey_package
test_msi_package
;;
esac
}
Expand Down
50 changes: 50 additions & 0 deletions scripts/build-os-packages/ggshield.wxs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Package
Name="ggshield"
Manufacturer="GitGuardian"
Version="$(var.Version)"
UpgradeCode="b0e3a5b8-6a44-4c2f-8c72-5f3e9a1d7b4e"
Scope="perMachine"
Compressed="yes"
>
<MajorUpgrade DowngradeErrorMessage="A newer version of ggshield is already installed." />

<MediaTemplate EmbedCab="yes" CompressionLevel="high" />

<StandardDirectory Id="ProgramFiles64Folder">
<Directory Id="GitGuardianDir" Name="GitGuardian">
<Directory Id="INSTALLFOLDER" Name="ggshield">
<Component
Id="MainExecutable"
Guid="a1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e"
>
<File Source="!(bindpath.SourceDir)\ggshield.exe" />
</Component>

<Component Id="PathEntry" Guid="d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f80">
<Environment
Id="PATH"
Name="PATH"
Value="[INSTALLFOLDER]"
Permanent="no"
Part="last"
Action="set"
System="yes"
/>
</Component>
<Directory Id="InternalDir" Name="_internal" />
</Directory>
</Directory>
</StandardDirectory>

<ComponentGroup Id="InternalFiles" Directory="InternalDir">
<Files Include="!(bindpath.SourceDir)\_internal\**" />
</ComponentGroup>

<Feature Id="Complete" Level="1">
<ComponentRef Id="MainExecutable" />
<ComponentGroupRef Id="InternalFiles" />
<ComponentRef Id="PathEntry" />
</Feature>
</Package>
</Wix>
46 changes: 46 additions & 0 deletions scripts/build-os-packages/windows-functions.bash
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Loading