From df44a9342e4ee5808ee8e7c20d2994786c4455e8 Mon Sep 17 00:00:00 2001 From: Scott Gress Date: Tue, 21 Apr 2026 08:53:46 -0700 Subject: [PATCH] Use Docker as default WiX runtime on macOS arm64 (#43715) **Related issue:** Resolves #43484 # Details Apple Silicon Macs were being forced down the Wine+local-wix-dir path because the fleetdm/wix:latest image was deemed unreliable on arm64 in Jan 2024. Docker Desktop's amd64 emulation has matured since -- the image builds both amd64 and arm64 MSIs on arm64 macOS successfully. This PR: - Drops the arm64-forces-Wine guard in BuildMSI so the Docker path is the default on every macOS arch when --local-wix-dir isn't provided. - Drops the macOS "Install wine and wix" + "Build MSI on macOS (using local Wix)" CI steps. The ubuntu-latest matrix entry already exercises the Docker path, and the install-wine.sh flow is brittle against Gcenx release churn and homebrew-cask deprecation. - Updates the install-wine.sh script to fail and output a message indicating that Docker should be used, or else Wine installed manually. ``` ============================================================ This script no longer installs Wine. ============================================================ Wine is no longer required to build Windows (.msi) packages on macOS. fleetctl package now uses Docker by default on all macOS architectures. RECOMMENDED: install Docker Desktop https://docs.docker.com/get-docker If you cannot use Docker and still need to build MSIs with Wine on macOS see the upstream WineHQ wiki for installation instructions: https://gitlab.winehq.org/wine/wine/-/wikis/MacOS Automatic Wine installation via Homebrew is no longer attempted here because the wine-stable cask is deprecated and upstream Wine releases have caused repeated breakage. ``` - Retains the wix auto-download helper (downloadAndExtractZip, extractZipFile, wixDownload) for backwards-compatibility when Docker isn't detected, with a deprecation warning. The Wine + --local-wix-dir path remains available for macOS users who opt into it, but is no longer documented. See #43484. # Checklist for submitter If some of the following don't apply, delete the relevant line. - [X] Changes file added for user-visible changes in `changes/`, `orbit/changes/` or `ee/fleetd-chrome/changes`. See [Changes files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files) for more information. ## Testing - [X] Added/updated automated tests - Dropped MacOS packaging tests. The Ubuntu test already exercises the Docker path that MacOS now uses. - [X] QA'd all new/changed functionality manually - Built and installed both amd64 and arm64 .msi packages successfully ## Summary by CodeRabbit * **New Features** * MSI packages on macOS now build using Docker by default, removing the Wine dependency. * **Documentation** * Updated macOS setup guidance: Docker Desktop is now required for MSI packaging instead of Wine. * **Chores** * Simplified Wine-related helper scripts and removed outdated installation logic. --- .github/workflows/test-packaging.yml | 14 ----- assets/scripts/install-wine.sh | 77 +++++++--------------------- changes/43484-msi-docker-on-arm64 | 1 + orbit/pkg/packaging/windows.go | 50 ++++++++++++------ orbit/pkg/packaging/wix/wix.go | 10 ++-- 5 files changed, 60 insertions(+), 92 deletions(-) create mode 100644 changes/43484-msi-docker-on-arm64 diff --git a/.github/workflows/test-packaging.yml b/.github/workflows/test-packaging.yml index ddd86552cd..c778aa7ab4 100644 --- a/.github/workflows/test-packaging.yml +++ b/.github/workflows/test-packaging.yml @@ -84,16 +84,6 @@ jobs: with: go-version-file: "go.mod" - - name: Install wine and wix - if: startsWith(matrix.os, 'macos') - run: | - ./assets/scripts/install-wine.sh -n - wget https://github.com/wixtoolset/wix3/releases/download/wix3112rtm/wix311-binaries.zip -nv -O wix.zip - mkdir wix - unzip wix.zip -d wix - rm -f wix.zip - echo wix installed at $(pwd)/wix - - name: Build fleetctl run: make fleetctl @@ -128,7 +118,3 @@ jobs: - name: Build PKG with Fleet Desktop run: ./build/fleetctl package --type pkg --enroll-secret=foo --fleet-url=https://localhost:8080 --fleet-desktop - - - name: Build MSI on macOS (using local Wix) - if: startsWith(matrix.os, 'macos') - run: ./build/fleetctl package --type msi --enroll-secret=foo --fleet-url=https://localhost:8080 --fleet-desktop --local-wix-dir ./wix diff --git a/assets/scripts/install-wine.sh b/assets/scripts/install-wine.sh index eb1e897abe..fd323603f0 100755 --- a/assets/scripts/install-wine.sh +++ b/assets/scripts/install-wine.sh @@ -1,68 +1,29 @@ #!/bin/bash -# Please don't delete. This script is linked to, as a redirect, from fleetctl and the Fleet website. +# Please don't delete. This script is linked to, as a redirect, from fleetctl and +# the Fleet website. It is preserved as an informational endpoint at +# https://fleetdm.com/install-wine so existing links don't 404. -set -eo pipefail +cat <<'EOF' +============================================================ +This script no longer installs Wine. +============================================================ -brew_wine(){ - # Wine reference: https://wiki.winehq.org/MacOS - # Wine can be installed without brew via a distribution such as https://github.com/Gcenx/macOS_Wine_builds/releases/tag/10.0 or by building from source. - # To install a version tied to a specific commit SHA we must create a local tap and install from it after wine 4.6.4 dropped support, see - # https://github.com/Homebrew/brew/pull/20414 - TAP_PATH="$(brew --repository)/Library/Taps/fleet/homebrew-local/Casks" - mkdir -p "${TAP_PATH}" - echo "# tap auto-generated by Fleet for installing wine-stable" > "$(dirname "${TAP_PATH}")/README.md" - curl -O https://raw.githubusercontent.com/Homebrew/homebrew-cask/8a5c4ae85cc2def6a1192779469170df3853e80c/Casks/w/wine-stable.rb - mv wine-stable.rb "${TAP_PATH}/" - brew install --cask fleet/local/wine-stable - xattr -dr com.apple.quarantine "$(brew --prefix)/Caskroom/wine-stable" - exit 0 -} +Wine is no longer required to build Windows (.msi) packages on macOS. +fleetctl package now uses Docker by default on all macOS architectures. +RECOMMENDED: install Docker Desktop + https://docs.docker.com/get-docker -warn_wine(){ -printf "\nWARNING: The Wine app developer has an Apple Developer certificate but the\napp bundle post-installation will not be code-signed or notarized.\n\nDo you wish to proceed?\n\n" -while true -do - read -r -p "install> " install - case "$install" in - y|yes|Y|YES) brew_wine ;; - n|no|N|NO) printf "\nExiting...\n\n"; exit 1 ;; - *) printf "\nPlease enter yes or no at the prompt...\n\n" ;; - esac -done -} +If you cannot use Docker and still need to build MSIs with Wine on macOS +see the upstream WineHQ wiki for installation instructions: + https://gitlab.winehq.org/wine/wine/-/wikis/MacOS +Automatic Wine installation via Homebrew is no longer attempted here +because the wine-stable cask is deprecated and upstream Wine releases +have caused repeated breakage. -# option to execute script in non-interactive mode -while getopts 'n' option -do - case "$option" in - n) mode=auto ;; - *) : ;; - esac -done +EOF - -# prevent root execution -if [ "$EUID" = 0 ] -then - printf "\nTo prevent unnecessary privilege elevation do not execute this script as the root user.\nExiting...\n\n"; exit 1 -fi - - -# check if Homebrew is installed -if ! command -v brew > /dev/null 2>&1 -then - printf "\nHomebrew is not installed.\nPlease install Homebrew.\nFor instructions, see https://brew.sh/\n\n"; exit 1 -fi - - -# install Wine -if [ "$mode" = 'auto' ] -then - printf "\n%s executed in non-interactive mode.\n\n" "$0"; brew_wine -else - warn_wine -fi +exit 1 diff --git a/changes/43484-msi-docker-on-arm64 b/changes/43484-msi-docker-on-arm64 new file mode 100644 index 0000000000..a1c2f5b194 --- /dev/null +++ b/changes/43484-msi-docker-on-arm64 @@ -0,0 +1 @@ +* Use Docker as the default WiX runtime on macOS (including Apple Silicon) when generating `.msi` packages via `fleetctl package`. Wine is no longer required on macOS for the default path. diff --git a/orbit/pkg/packaging/windows.go b/orbit/pkg/packaging/windows.go index ebf7f4368b..0675a82ecf 100644 --- a/orbit/pkg/packaging/windows.go +++ b/orbit/pkg/packaging/windows.go @@ -180,24 +180,29 @@ func BuildMSI(opt Options) (string, error) { absWixDir := opt.LocalWixDir wineChecked := false - // Download wix for macOS running on arm64, unless a local-wix-dir is provided. - // We are using native MSI build on macOS arm64, instead of Docker, because the current fleetdm/wix Docker image is unreliable on macOS arm64. - // We are looking into creating a new Docker image for macOS arm64. - if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" && absWixDir == "" { - fmt.Println("Detected macOS arm64. fleetctl must use locally installed wine and wix to build the MSI package.") + // On macOS without --local-wix-dir, the default path uses Docker. + // For backwards compatibility with existing pipelines that rely + // on the legacy Wine + auto-downloaded WiX flow, fall back to that + // path if Docker isn't available — but warn that it's deprecated. + if runtime.GOOS == "darwin" && !opt.NativeTooling && absWixDir == "" { + if dockerErr := checkDockerAvailable(); dockerErr != nil { + fmt.Printf("\nWARNING: Docker is not available (%s).\n", dockerErr) + fmt.Println("Falling back to Wine + auto-downloaded WiX toolset. This path is deprecated") + fmt.Println("and will be removed in a future release. Install Docker Desktop to use the") + fmt.Println("supported path: https://docs.docker.com/get-docker") + fmt.Println() - // Ensure wine is installed before downloading wix - if err = checkWine(false); err != nil { - return "", err - } - wineChecked = true + if err = checkWine(false); err != nil { + return "", err + } + wineChecked = true - fmt.Printf("Downloading wix from %s\n", wixDownload) - client := fleethttp.NewClient() - absWixDir = filepath.Join(tmpDir, "wix") - err = downloadAndExtractZip(client, wixDownload, absWixDir) - if err != nil { - return "", err + fmt.Printf("Downloading wix from %s\n", wixDownload) + client := fleethttp.NewClient() + absWixDir = filepath.Join(tmpDir, "wix") + if err = downloadAndExtractZip(client, wixDownload, absWixDir); err != nil { + return "", err + } } } @@ -250,7 +255,7 @@ func checkWine(wineChecked bool) error { cmd := exec.Command(wix.WineCmd, "--version") if err := cmd.Run(); err != nil { return fmt.Errorf( - "%s failed. Is Wine installed? Creating a fleetd agent for Windows (.msi) requires Wine. To install Wine see the script here: https://fleetdm.com/install-wine %w", + "%s failed. Is Wine installed? %w", wix.WineCmd, err, ) } @@ -258,6 +263,17 @@ func checkWine(wineChecked bool) error { return nil } +// checkDockerAvailable returns nil if the docker CLI is on PATH and the Docker +// daemon is reachable. Otherwise it returns an error summarizing what went +// wrong, which callers can use to decide whether to fall back to another path. +func checkDockerAvailable() error { + cmd := exec.Command("docker", "version") + if err := cmd.Run(); err != nil { + return err + } + return nil +} + func writeWixFile(opt Options, rootPath string) error { // PackageInfo is metadata for the pkg path := filepath.Join(rootPath, "main.wxs") diff --git a/orbit/pkg/packaging/wix/wix.go b/orbit/pkg/packaging/wix/wix.go index 70a9047192..362f514946 100644 --- a/orbit/pkg/packaging/wix/wix.go +++ b/orbit/pkg/packaging/wix/wix.go @@ -33,6 +33,7 @@ func Heat(path string, native bool, localWixDir string) error { args = append( args, "docker", "run", "--rm", "--platform", dockerPlatform, + "--env", "XDG_RUNTIME_DIR=/tmp", // silence Wine warning inside container "--volume", path+":/wix", // mount volume imageName, // image name ) @@ -79,17 +80,18 @@ func Heat(path string, native bool, localWixDir string) error { } func darwinWineExecutable() (string, error) { + wineWarning := "Is Wine installed? Building a Windows (.msi) package with --local-wix-dir on macOS requires Wine; the default path uses Docker instead." cmdOut, err := exec.Command("wine", "--version").Output() if err != nil { - return "", fmt.Errorf("running wine to get version information: %w", err) + return "", fmt.Errorf("running wine to get version information: %w. %s", err, wineWarning) } wineVerStr, found := strings.CutPrefix(string(cmdOut), "wine-") if !found { - return "", fmt.Errorf("Unknown wine version: %q. Is Wine installed? Creating a fleetd agent for Windows (.msi) requires Wine. To install Wine see the script here: https://fleetdm.com/install-wine ", string(cmdOut)) + return "", fmt.Errorf("Unknown wine version: %q. %s", string(cmdOut), wineWarning) } wineVersion, err := strconv.ParseInt(strings.Split(wineVerStr, ".")[0], 10, 64) if err != nil { - return "", fmt.Errorf("Unable to parse wine version: %q. Is Wine installed? Creating a fleetd agent for Windows (.msi) requires Wine. To install Wine see the script here: https://fleetdm.com/install-wine ", wineVerStr) + return "", fmt.Errorf("Unable to parse wine version: %q. %s", wineVerStr, wineWarning) } if wineVersion < 10 { return Wine64Cmd, nil @@ -108,6 +110,7 @@ func Candle(path string, native bool, localWixDir string, arch string) error { args = append( args, "docker", "run", "--rm", "--platform", dockerPlatform, + "--env", "XDG_RUNTIME_DIR=/tmp", // silence Wine warning inside container "--volume", path+":/wix", // mount volume imageName, // image name ) @@ -165,6 +168,7 @@ func Light(path string, native bool, localWixDir string) error { args = append( args, "docker", "run", "--rm", "--platform", dockerPlatform, + "--env", "XDG_RUNTIME_DIR=/tmp", // silence Wine warning inside container "--volume", path+":/wix", // mount volume imageName, // image name )