From 3b59fd8fd4ac5049e36fcb69289b4ed300d6c241 Mon Sep 17 00:00:00 2001 From: James Pickett Date: Wed, 24 Jul 2024 16:05:17 -0700 Subject: [PATCH 01/10] build msi for arm and amd --- cmd/launcher/svc_config_windows.go | 56 +++++++++++++++++ cmd/package-builder/package-builder.go | 54 +++++++++------- pkg/packagekit/wix/service.go | 54 ++++------------ pkg/packagekit/wix/service_test.go | 11 +++- pkg/packagekit/wix/wix.go | 63 +++++++++++++++++++ pkg/packaging/detectLauncherVersion.go | 2 +- pkg/packaging/fetch.go | 8 +++ pkg/packaging/packaging.go | 86 +++++++++++++++++--------- 8 files changed, 237 insertions(+), 97 deletions(-) diff --git a/cmd/launcher/svc_config_windows.go b/cmd/launcher/svc_config_windows.go index 1d7ee7a62..a3a3ab69f 100644 --- a/cmd/launcher/svc_config_windows.go +++ b/cmd/launcher/svc_config_windows.go @@ -6,6 +6,7 @@ package main import ( "context" "log/slog" + "time" "github.com/kolide/launcher/pkg/launcher" @@ -64,6 +65,8 @@ func checkServiceConfiguration(logger *slog.Logger, opts *launcher.Options) { checkDependOnService(launcherServiceKey, logger) checkRestartActions(logger) + + setRecoveryActions(logger) } // checkDelayedAutostart checks the current value of `DelayedAutostart` (whether to wait ~2 minutes @@ -184,3 +187,56 @@ func checkRestartActions(logger *slog.Logger) { logger.Log(context.TODO(), slog.LevelInfo, "successfully set RecoveryActionsOnNonCrashFailures flag") } + +// setRecoveryActions sets the recovery actions for the launcher service. +// previously defined via wix ServicConfig Element (Util Extension) https://wixtoolset.org/docs/v3/xsd/util/serviceconfig/ +func setRecoveryActions(logger *slog.Logger) { + sman, err := mgr.Connect() + if err != nil { + logger.Log(context.TODO(), slog.LevelError, + "connecting to service control manager", + "err", err, + ) + + return + } + + defer sman.Disconnect() + + launcherService, err := sman.OpenService(launcherServiceName) + if err != nil { + logger.Log(context.TODO(), slog.LevelError, + "opening the launcher service from control manager", + "err", err, + ) + + return + } + + defer launcherService.Close() + + recoveryActions := []mgr.RecoveryAction{ + { + // first failure + Type: mgr.ServiceRestart, + Delay: 5 * time.Second, + }, + { + // second failure + Type: mgr.ServiceRestart, + Delay: 5 * time.Second, + }, + { + // subsequent failures + Type: mgr.ServiceRestart, + Delay: 5 * time.Second, + }, + } + + if err := launcherService.SetRecoveryActions(recoveryActions, 24*60*60); err != nil { // 24 hours + logger.Log(context.TODO(), slog.LevelError, + "setting RecoveryActions", + "err", err, + ) + } +} diff --git a/cmd/package-builder/package-builder.go b/cmd/package-builder/package-builder.go index fd79b3fb5..36bb323e6 100644 --- a/cmd/package-builder/package-builder.go +++ b/cmd/package-builder/package-builder.go @@ -79,6 +79,11 @@ func runMake(args []string) error { env.String("LAUNCHER_VERSION", "stable"), "What TUF channel to download launcher from. Supports filesystem paths", ) + flLauncherArmVersion = flagset.String( + "launcher_arm_version", + env.String("LAUNCHER_ARM_VERSION", "stable"), + "What TUF channel to download launcher from for ARM. Supports filesystem paths", + ) flExtensionVersion = flagset.String( "extension_version", env.String("EXTENSION_VERSION", "stable"), @@ -230,30 +235,31 @@ func runMake(args []string) error { } packageOptions := packaging.PackageOptions{ - PackageVersion: *flPackageVersion, - OsqueryVersion: *flOsqueryVersion, - OsqueryFlags: flOsqueryFlags, - LauncherVersion: *flLauncherVersion, - ExtensionVersion: *flExtensionVersion, - Hostname: *flHostname, - Secret: *flEnrollSecret, - AppleSigningKey: *flSigningKey, - Transport: *flTransport, - Insecure: *flInsecure, - InsecureTransport: *flInsecureTransport, - UpdateChannel: *flUpdateChannel, - InitialRunner: *flInitialRunner, - Identifier: *flIdentifier, - OmitSecret: *flOmitSecret, - CertPins: *flCertPins, - RootPEM: *flRootPEM, - BinRootDir: *flBinRootDir, - CacheDir: cacheDir, - TufServerURL: *flTufURL, - MirrorURL: *flMirrorURL, - WixPath: *flWixPath, - WixSkipCleanup: *flWixSkipCleanup, - DisableService: *flDisableService, + PackageVersion: *flPackageVersion, + OsqueryVersion: *flOsqueryVersion, + OsqueryFlags: flOsqueryFlags, + LauncherVersion: *flLauncherVersion, + LauncherArmVersion: *flLauncherArmVersion, + ExtensionVersion: *flExtensionVersion, + Hostname: *flHostname, + Secret: *flEnrollSecret, + AppleSigningKey: *flSigningKey, + Transport: *flTransport, + Insecure: *flInsecure, + InsecureTransport: *flInsecureTransport, + UpdateChannel: *flUpdateChannel, + InitialRunner: *flInitialRunner, + Identifier: *flIdentifier, + OmitSecret: *flOmitSecret, + CertPins: *flCertPins, + RootPEM: *flRootPEM, + BinRootDir: *flBinRootDir, + CacheDir: cacheDir, + TufServerURL: *flTufURL, + MirrorURL: *flMirrorURL, + WixPath: *flWixPath, + WixSkipCleanup: *flWixSkipCleanup, + DisableService: *flDisableService, } outputDir := *flOutputDir diff --git a/pkg/packagekit/wix/service.go b/pkg/packagekit/wix/service.go index 131fa399e..f6b42bc55 100644 --- a/pkg/packagekit/wix/service.go +++ b/pkg/packagekit/wix/service.go @@ -62,7 +62,6 @@ type ServiceInstall struct { Start StartType `xml:",attr,omitempty"` Type string `xml:",attr,omitempty"` Vital YesNoType `xml:",attr,omitempty"` // The overall install should fail if this service fails to install - UtilServiceConfig *UtilServiceConfig `xml:",omitempty"` ServiceConfig *ServiceConfig `xml:",omitempty"` ServiceDependency *ServiceDependency `xml:",omitempty"` } @@ -90,6 +89,7 @@ type ServiceControl struct { // This is used needed to set DelayedAutoStart type ServiceConfig struct { // TODO: this should need a namespace, and yet. See https://github.com/golang/go/issues/36813 + Id string `xml:",attr,omitempty"` XMLName xml.Name `xml:"http://schemas.microsoft.com/wix/2006/wi ServiceConfig"` DelayedAutoStart YesNoType `xml:",attr,omitempty"` OnInstall YesNoType `xml:",attr,omitempty"` @@ -97,25 +97,6 @@ type ServiceConfig struct { OnUninstall YesNoType `xml:",attr,omitempty"` } -// UtilServiceConfig implements -// http://wixtoolset.org/documentation/manual/v3/xsd/util/serviceconfig.html -// This is used to set FailureActions. There are some -// limitations. Notably, reset period is in days here, though the -// underlying `sc.exe` command supports seconds. (See -// https://github.com/wixtoolset/issues/issues/5963) -// -// Docs are a bit confusing. This schema is supported, and should -// work. The non-util ServiceConfig generates unsupported CNDL1150 -// errors. -type UtilServiceConfig struct { - XMLName xml.Name `xml:"http://schemas.microsoft.com/wix/UtilExtension ServiceConfig"` - FirstFailureActionType string `xml:",attr,omitempty"` - SecondFailureActionType string `xml:",attr,omitempty"` - ThirdFailureActionType string `xml:",attr,omitempty"` - RestartServiceDelayInSeconds int `xml:",attr,omitempty"` - ResetPeriodInDays int `xml:",attr,omitempty"` -} - // Service represents a wix service. It provides an interface to both // ServiceInstall and ServiceControl. type Service struct { @@ -194,16 +175,6 @@ func ServiceArgs(args []string) ServiceOpt { // New returns a service func NewService(matchString string, opts ...ServiceOpt) *Service { - // Set some defaults. It's not clear we can reset in under a - // day. See https://github.com/wixtoolset/issues/issues/5963 - utilServiceConfig := &UtilServiceConfig{ - FirstFailureActionType: "restart", - SecondFailureActionType: "restart", - ThirdFailureActionType: "restart", - ResetPeriodInDays: 1, - RestartServiceDelayInSeconds: 5, - } - serviceConfig := &ServiceConfig{ OnInstall: Yes, OnReinstall: Yes, @@ -215,16 +186,16 @@ func NewService(matchString string, opts ...ServiceOpt) *Service { // probably better to specific a ServiceName, but this might be an // okay default. defaultName := cleanServiceName(strings.TrimSuffix(matchString, ".exe") + ".svc") + si := &ServiceInstall{ - Name: defaultName, - Id: defaultName, - Account: `[SERVICEACCOUNT]`, // Wix resolves this to `LocalSystem` - Start: StartAuto, - Type: "ownProcess", - ErrorControl: ErrorControlNormal, - Vital: Yes, - UtilServiceConfig: utilServiceConfig, - ServiceConfig: serviceConfig, + Name: defaultName, + Id: defaultName, + Account: `[SERVICEACCOUNT]`, // Wix resolves this to `LocalSystem` + Start: StartAuto, + Type: "ownProcess", + ErrorControl: ErrorControlNormal, + Vital: Yes, + ServiceConfig: serviceConfig, } sc := &ServiceControl{ @@ -237,8 +208,9 @@ func NewService(matchString string, opts ...ServiceOpt) *Service { } s := &Service{ - matchString: matchString, - expectedCount: 1, + matchString: matchString, + // one count for arm64 and ond for amd64 + expectedCount: 2, count: 0, serviceInstall: si, serviceControl: sc, diff --git a/pkg/packagekit/wix/service_test.go b/pkg/packagekit/wix/service_test.go index 340b440d9..d1d0306d6 100644 --- a/pkg/packagekit/wix/service_test.go +++ b/pkg/packagekit/wix/service_test.go @@ -17,17 +17,22 @@ func TestService(t *testing.T) { require.NoError(t, err) require.False(t, expectFalse) + // first match expectTrue, err := service.Match("daemon.exe") require.NoError(t, err) require.True(t, expectTrue) - // Should error. count now exceeds expectedCount. + // second match expectTrue2, err := service.Match("daemon.exe") - require.Error(t, err) + require.NoError(t, err) require.True(t, expectTrue2) + // third match, should error + expectTrue3, err := service.Match("daemon.exe") + require.Error(t, err) + require.True(t, expectTrue3) + expectedXml := ` - ` diff --git a/pkg/packagekit/wix/wix.go b/pkg/packagekit/wix/wix.go index 4528e78b1..d72dc3ddd 100644 --- a/pkg/packagekit/wix/wix.go +++ b/pkg/packagekit/wix/wix.go @@ -12,6 +12,7 @@ import ( "github.com/go-kit/kit/log/level" "github.com/kolide/kit/fsutil" + "github.com/kolide/kit/ulid" "github.com/kolide/launcher/pkg/contexts/ctxlog" ) @@ -223,19 +224,81 @@ func (wo *wixTool) addServices(ctx context.Context) error { } defer heatWrite.Close() + type archSpecificBinDir string + + const ( + none archSpecificBinDir = "" + amd64 archSpecificBinDir = "amd64" + arm64 archSpecificBinDir = "arm64" + ) + currentArchSpecificBinDir := none + + baseSvcName := wo.services[0].serviceInstall.Id + lines := strings.Split(string(heatContent), "\n") for _, line := range lines { + + if currentArchSpecificBinDir != none && strings.Contains(line, "") { + // were in a arch specific bin dir that we want to remove, don't write closing tag + currentArchSpecificBinDir = none + continue + } + + if strings.Contains(line, "Directory") { + if strings.Contains(line, string(amd64)) { + // were in a arch specific bin dir that we want to remove so when we hit closing tag, we'll skip it + currentArchSpecificBinDir = amd64 + continue + } + + if strings.Contains(line, string(arm64)) { + // were in a arch specific bin dir that we want to remove so when we hit closing tag, we'll skip it + currentArchSpecificBinDir = arm64 + continue + } + } + heatWrite.WriteString(line) heatWrite.WriteString("\n") + for _, service := range wo.services { + isMatch, err := service.Match(line) if err != nil { return fmt.Errorf("match error: %w", err) } + if isMatch { + if currentArchSpecificBinDir == none { + return fmt.Errorf("service found, but not in a bin directory") + } + + // make sure elements are not duplicated in any service + serviceId := fmt.Sprintf("%s%s", baseSvcName, ulid.New()) + service.serviceControl.Id = serviceId + service.serviceInstall.Id = serviceId + service.serviceInstall.ServiceConfig.Id = serviceId + + // create a condition based on architecture + // have to format in the "%P" in "%PROCESSOR_ARCHITECTURE" + heatWrite.WriteString(fmt.Sprintf(` %sROCESSOR_ARCHITECTURE="%s" `, "%P", strings.ToUpper(string(currentArchSpecificBinDir)))) + heatWrite.WriteString("\n") + if err := service.Xml(heatWrite); err != nil { return fmt.Errorf("adding service: %w", err) } + + continue + } + + if strings.Contains(line, "osqueryd.exe") { + if currentArchSpecificBinDir == none { + return fmt.Errorf("osqueryd.exe found, but not in a bin directory") + } + + // create a condition based on architecture + heatWrite.WriteString(fmt.Sprintf(` %sROCESSOR_ARCHITECTURE="%s" `, "%P", currentArchSpecificBinDir)) + heatWrite.WriteString("\n") } } } diff --git a/pkg/packaging/detectLauncherVersion.go b/pkg/packaging/detectLauncherVersion.go index f16c9a85b..3c4050f7c 100644 --- a/pkg/packaging/detectLauncherVersion.go +++ b/pkg/packaging/detectLauncherVersion.go @@ -19,7 +19,7 @@ func (p *PackageOptions) detectLauncherVersion(ctx context.Context) error { logger := log.With(ctxlog.FromContext(ctx), "library", "detectLauncherVersion") level.Debug(logger).Log("msg", "Attempting launcher autodetection") - launcherPath := p.launcherLocation(filepath.Join(p.packageRoot, p.binDir)) + launcherPath := p.launcherLocation(filepath.Join(p.packageRoot, p.binDir, string(p.target.Arch))) stdout, err := p.execOut(ctx, launcherPath, "-version") if err != nil { return fmt.Errorf("failed to exec -- possibly can't autodetect while cross compiling: out `%s`: %w", stdout, err) diff --git a/pkg/packaging/fetch.go b/pkg/packaging/fetch.go index 0c7263152..7688cd6bb 100644 --- a/pkg/packaging/fetch.go +++ b/pkg/packaging/fetch.go @@ -38,6 +38,14 @@ func FetchBinary(ctx context.Context, localCacheDir, name, binaryName, channelOr return "", errors.New("empty cache dir argument") } + // put binaries in arch specific directory to avoid naming collisions in wix msi building + // where a single destination will have multiple, mutally exclusive sources + localCacheDir = filepath.Join(localCacheDir, string(target.Arch)) + + if err := os.MkdirAll(localCacheDir, fsutil.DirMode); err != nil { + return "", fmt.Errorf("could not create cache directory: %w", err) + } + localBinaryPath := filepath.Join(localCacheDir, fmt.Sprintf("%s-%s-%s", name, target.Platform, channelOrVersion), binaryName) localPackagePath := filepath.Join(localCacheDir, fmt.Sprintf("%s-%s-%s.tar.gz", name, target.Platform, channelOrVersion)) diff --git a/pkg/packaging/packaging.go b/pkg/packaging/packaging.go index 0bba3ce61..5b18a8a4a 100644 --- a/pkg/packaging/packaging.go +++ b/pkg/packaging/packaging.go @@ -32,37 +32,39 @@ const ( // PackageOptions encapsulates the launcher build options. It's // populated by callers, such as command line flags. It may change. type PackageOptions struct { - PackageVersion string // What version in this package. If unset, autodetection will be attempted. - OsqueryVersion string - OsqueryFlags []string // Additional flags to pass to the runtime osquery instance - LauncherVersion string - ExtensionVersion string - Hostname string - Secret string - Transport string - Insecure bool - InsecureTransport bool - UpdateChannel string - InitialRunner bool - Identifier string - Title string - OmitSecret bool - CertPins string - RootPEM string - BinRootDir string - CacheDir string - TufServerURL string - MirrorURL string - WixPath string - MSIUI bool - WixSkipCleanup bool - DisableService bool + PackageVersion string // What version in this package. If unset, autodetection will be attempted. + OsqueryVersion string + OsqueryFlags []string // Additional flags to pass to the runtime osquery instance + LauncherVersion string + LauncherArmVersion string + ExtensionVersion string + Hostname string + Secret string + Transport string + Insecure bool + InsecureTransport bool + UpdateChannel string + InitialRunner bool + Identifier string + Title string + OmitSecret bool + CertPins string + RootPEM string + BinRootDir string + CacheDir string + TufServerURL string + MirrorURL string + WixPath string + MSIUI bool + WixSkipCleanup bool + DisableService bool // Normally we'd download the same version we bake into the // autoupdate. But occasionally, it's handy to make a package // with a different version. - LauncherDownloadVersionOverride string - OsqueryDownloadVersionOverride string + LauncherDownloadVersionOverride string + LauncherArmDownloadVersionOverride string + OsqueryDownloadVersionOverride string AppleNotarizeAccountId string // The 10 character apple account id AppleNotarizeAppPassword string // app password for notarization service @@ -231,6 +233,29 @@ func (p *PackageOptions) Build(ctx context.Context, packageWriter io.Writer, tar return fmt.Errorf("fetching binary launcher: %w", err) } + // for windows, make a separate target for arm64 + if p.target.Platform == Windows { + // make a copy of P + packageOptsCopy := *p + packageOptsCopy.target.Arch = Arm64 + + if packageOptsCopy.OsqueryDownloadVersionOverride == "" { + packageOptsCopy.OsqueryDownloadVersionOverride = packageOptsCopy.OsqueryVersion + } + + if err := packageOptsCopy.getBinary(ctx, "osqueryd", packageOptsCopy.target.PlatformBinaryName("osqueryd"), packageOptsCopy.OsqueryDownloadVersionOverride); err != nil { + return fmt.Errorf("fetching binary osqueryd: %w", err) + } + + if packageOptsCopy.LauncherArmDownloadVersionOverride == "" { + packageOptsCopy.LauncherDownloadVersionOverride = packageOptsCopy.LauncherArmVersion + } + + if err := packageOptsCopy.getBinary(ctx, "launcher", packageOptsCopy.target.PlatformBinaryName("launcher"), packageOptsCopy.LauncherDownloadVersionOverride); err != nil { + return fmt.Errorf("fetching binary launcher: %w", err) + } + } + // Some darwin specific bits if p.target.Platform == Darwin { if err := p.renderNewSyslogConfig(ctx); err != nil { @@ -366,10 +391,15 @@ func (p *PackageOptions) getBinary(ctx context.Context, symbolicName, binaryName return nil } + binPath := filepath.Join(p.packageRoot, p.binDir, string(p.target.Arch), binaryName) + if err := os.MkdirAll(filepath.Dir(binPath), fsutil.DirMode); err != nil { + return fmt.Errorf("could not create directory for binary %s: %w", binaryName, err) + } + // Not an app bundle -- just copy the binary. if err := fsutil.CopyFile( localPath, - filepath.Join(p.packageRoot, p.binDir, binaryName), + binPath, ); err != nil { return fmt.Errorf("could not copy binary %s: %w", binaryName, err) } From eca65589364587b8455cf632f0898fbe9fb47a7f Mon Sep 17 00:00:00 2001 From: James Pickett Date: Thu, 25 Jul 2024 08:21:18 -0700 Subject: [PATCH 02/10] look for app bundle on darwin --- pkg/packaging/detectLauncherVersion.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pkg/packaging/detectLauncherVersion.go b/pkg/packaging/detectLauncherVersion.go index 3c4050f7c..3176acba4 100644 --- a/pkg/packaging/detectLauncherVersion.go +++ b/pkg/packaging/detectLauncherVersion.go @@ -19,7 +19,13 @@ func (p *PackageOptions) detectLauncherVersion(ctx context.Context) error { logger := log.With(ctxlog.FromContext(ctx), "library", "detectLauncherVersion") level.Debug(logger).Log("msg", "Attempting launcher autodetection") - launcherPath := p.launcherLocation(filepath.Join(p.packageRoot, p.binDir, string(p.target.Arch))) + binDir := filepath.Join(p.packageRoot, p.binDir) + if p.target.Platform != Darwin { + binDir = filepath.Join(binDir, string(p.target.Arch)) + } + + launcherPath := p.launcherLocation(binDir) + stdout, err := p.execOut(ctx, launcherPath, "-version") if err != nil { return fmt.Errorf("failed to exec -- possibly can't autodetect while cross compiling: out `%s`: %w", stdout, err) From 2fbeac339070591805ad87b795e1ae990d5ed9bc Mon Sep 17 00:00:00 2001 From: James Pickett Date: Thu, 25 Jul 2024 14:45:53 -0700 Subject: [PATCH 03/10] tweaks and comments --- pkg/packagekit/wix/service.go | 2 +- pkg/packagekit/wix/wix.go | 10 +++++++--- pkg/packaging/detectLauncherVersion.go | 12 ++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/pkg/packagekit/wix/service.go b/pkg/packagekit/wix/service.go index f6b42bc55..3335816d5 100644 --- a/pkg/packagekit/wix/service.go +++ b/pkg/packagekit/wix/service.go @@ -209,7 +209,7 @@ func NewService(matchString string, opts ...ServiceOpt) *Service { s := &Service{ matchString: matchString, - // one count for arm64 and ond for amd64 + // one count for arm64, one for amd64 expectedCount: 2, count: 0, serviceInstall: si, diff --git a/pkg/packagekit/wix/wix.go b/pkg/packagekit/wix/wix.go index d72dc3ddd..d92aa890a 100644 --- a/pkg/packagekit/wix/wix.go +++ b/pkg/packagekit/wix/wix.go @@ -244,15 +244,19 @@ func (wo *wixTool) addServices(ctx context.Context) error { continue } - if strings.Contains(line, "Directory") { + // the directory tag will like like "" + // so we just check for the first part of the string + if strings.Contains(line, " Date: Thu, 25 Jul 2024 14:59:28 -0700 Subject: [PATCH 04/10] fix tests --- pkg/packaging/detectLauncherVersion_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/packaging/detectLauncherVersion_test.go b/pkg/packaging/detectLauncherVersion_test.go index 5eebfd0a5..3f7e6f4a9 100644 --- a/pkg/packaging/detectLauncherVersion_test.go +++ b/pkg/packaging/detectLauncherVersion_test.go @@ -95,7 +95,7 @@ func TestLauncherLocation(t *testing.T) { // Create a temp directory with an app bundle in it tmpDir := t.TempDir() - binDir := filepath.Join(tmpDir, "bin") + binDir := filepath.Join(tmpDir, "bin", "universal") require.NoError(t, os.MkdirAll(binDir, 0755)) baseDir := filepath.Join(tmpDir, "Kolide.app", "Contents", "MacOS") require.NoError(t, os.MkdirAll(baseDir, 0755)) From 5f89750e85c1d6bf94aa723dd2e9937948ab360b Mon Sep 17 00:00:00 2001 From: James Pickett Date: Fri, 26 Jul 2024 09:10:44 -0700 Subject: [PATCH 05/10] remove unused variables, comments, feedback --- cmd/package-builder/package-builder.go | 2 ++ pkg/packagekit/wix/service.go | 5 +++++ pkg/packaging/packaging.go | 31 ++++---------------------- 3 files changed, 11 insertions(+), 27 deletions(-) diff --git a/cmd/package-builder/package-builder.go b/cmd/package-builder/package-builder.go index 36bb323e6..cfcaaea1d 100644 --- a/cmd/package-builder/package-builder.go +++ b/cmd/package-builder/package-builder.go @@ -79,6 +79,8 @@ func runMake(args []string) error { env.String("LAUNCHER_VERSION", "stable"), "What TUF channel to download launcher from. Supports filesystem paths", ) + // flLauncherArmVersion primarily exists to be able to provide the path to a local launcher binary + // for testing flLauncherArmVersion = flagset.String( "launcher_arm_version", env.String("LAUNCHER_ARM_VERSION", "stable"), diff --git a/pkg/packagekit/wix/service.go b/pkg/packagekit/wix/service.go index 3335816d5..fe452b648 100644 --- a/pkg/packagekit/wix/service.go +++ b/pkg/packagekit/wix/service.go @@ -89,6 +89,11 @@ type ServiceControl struct { // This is used needed to set DelayedAutoStart type ServiceConfig struct { // TODO: this should need a namespace, and yet. See https://github.com/golang/go/issues/36813 + + // Id will be automaticlly set to the parent ServiceName attribute if not set. + // This will result in an error from wix if there are multiple services with the same name + // that occurs when we are creating an MSI with both arm64 and amd64 binaries. + // So we set Id in the heat post processing step. Id string `xml:",attr,omitempty"` XMLName xml.Name `xml:"http://schemas.microsoft.com/wix/2006/wi ServiceConfig"` DelayedAutoStart YesNoType `xml:",attr,omitempty"` diff --git a/pkg/packaging/packaging.go b/pkg/packaging/packaging.go index 5b18a8a4a..44fc08f2d 100644 --- a/pkg/packaging/packaging.go +++ b/pkg/packaging/packaging.go @@ -59,13 +59,6 @@ type PackageOptions struct { WixSkipCleanup bool DisableService bool - // Normally we'd download the same version we bake into the - // autoupdate. But occasionally, it's handy to make a package - // with a different version. - LauncherDownloadVersionOverride string - LauncherArmDownloadVersionOverride string - OsqueryDownloadVersionOverride string - AppleNotarizeAccountId string // The 10 character apple account id AppleNotarizeAppPassword string // app password for notarization service AppleNotarizeUserId string // User id to authenticate to the notarization service with @@ -217,19 +210,11 @@ func (p *PackageOptions) Build(ctx context.Context, packageWriter io.Writer, tar // Install binaries into packageRoot // TODO parallization // TODO windows file extensions - - if p.OsqueryDownloadVersionOverride == "" { - p.OsqueryDownloadVersionOverride = p.OsqueryVersion - } - if err := p.getBinary(ctx, "osqueryd", p.target.PlatformBinaryName("osqueryd"), p.OsqueryDownloadVersionOverride); err != nil { + if err := p.getBinary(ctx, "osqueryd", p.target.PlatformBinaryName("osqueryd"), p.OsqueryVersion); err != nil { return fmt.Errorf("fetching binary osqueryd: %w", err) } - if p.LauncherDownloadVersionOverride == "" { - p.LauncherDownloadVersionOverride = p.LauncherVersion - } - - if err := p.getBinary(ctx, "launcher", p.target.PlatformBinaryName("launcher"), p.LauncherDownloadVersionOverride); err != nil { + if err := p.getBinary(ctx, "launcher", p.target.PlatformBinaryName("launcher"), p.LauncherVersion); err != nil { return fmt.Errorf("fetching binary launcher: %w", err) } @@ -239,19 +224,11 @@ func (p *PackageOptions) Build(ctx context.Context, packageWriter io.Writer, tar packageOptsCopy := *p packageOptsCopy.target.Arch = Arm64 - if packageOptsCopy.OsqueryDownloadVersionOverride == "" { - packageOptsCopy.OsqueryDownloadVersionOverride = packageOptsCopy.OsqueryVersion - } - - if err := packageOptsCopy.getBinary(ctx, "osqueryd", packageOptsCopy.target.PlatformBinaryName("osqueryd"), packageOptsCopy.OsqueryDownloadVersionOverride); err != nil { + if err := packageOptsCopy.getBinary(ctx, "osqueryd", packageOptsCopy.target.PlatformBinaryName("osqueryd"), packageOptsCopy.OsqueryVersion); err != nil { return fmt.Errorf("fetching binary osqueryd: %w", err) } - if packageOptsCopy.LauncherArmDownloadVersionOverride == "" { - packageOptsCopy.LauncherDownloadVersionOverride = packageOptsCopy.LauncherArmVersion - } - - if err := packageOptsCopy.getBinary(ctx, "launcher", packageOptsCopy.target.PlatformBinaryName("launcher"), packageOptsCopy.LauncherDownloadVersionOverride); err != nil { + if err := packageOptsCopy.getBinary(ctx, "launcher", packageOptsCopy.target.PlatformBinaryName("launcher"), packageOptsCopy.LauncherArmVersion); err != nil { return fmt.Errorf("fetching binary launcher: %w", err) } } From 80c3def89d1df6c5b64392ac36f02a8a0f340b61 Mon Sep 17 00:00:00 2001 From: James Pickett Date: Fri, 26 Jul 2024 11:47:01 -0700 Subject: [PATCH 06/10] default launcher_arm_version flag to value of launcher_version if not set --- cmd/package-builder/package-builder.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/cmd/package-builder/package-builder.go b/cmd/package-builder/package-builder.go index cfcaaea1d..effac1b09 100644 --- a/cmd/package-builder/package-builder.go +++ b/cmd/package-builder/package-builder.go @@ -79,12 +79,12 @@ func runMake(args []string) error { env.String("LAUNCHER_VERSION", "stable"), "What TUF channel to download launcher from. Supports filesystem paths", ) - // flLauncherArmVersion primarily exists to be able to provide the path to a local launcher binary + // flLauncherArmVersion primarily exists to be able to provide the path to a local launcher arm binary // for testing flLauncherArmVersion = flagset.String( "launcher_arm_version", - env.String("LAUNCHER_ARM_VERSION", "stable"), - "What TUF channel to download launcher from for ARM. Supports filesystem paths", + "", + "What TUF channel to download launcher from for ARM. Supports filesystem paths, defaults to value of launcher_version", ) flExtensionVersion = flagset.String( "extension_version", @@ -264,6 +264,10 @@ func runMake(args []string) error { DisableService: *flDisableService, } + if packageOptions.LauncherArmVersion == "" { + packageOptions.LauncherArmVersion = packageOptions.LauncherVersion + } + outputDir := *flOutputDir // NOTE: if you are using docker-for-mac, you probably need to set the TMPDIR env to /tmp From de4544b439fb2c02c99db534e5a6cfdb24666ae6 Mon Sep 17 00:00:00 2001 From: James Pickett Date: Mon, 29 Jul 2024 15:24:14 -0700 Subject: [PATCH 07/10] separate flags for local bin and version, print skipped clean up dirs --- cmd/launcher/svc_config_windows.go | 4 +- cmd/package-builder/package-builder.go | 70 +++++++++++++------------- pkg/packagekit/wix/wix.go | 6 +++ pkg/packaging/packaging.go | 67 +++++++++++++----------- 4 files changed, 83 insertions(+), 64 deletions(-) diff --git a/cmd/launcher/svc_config_windows.go b/cmd/launcher/svc_config_windows.go index 34382c285..941514468 100644 --- a/cmd/launcher/svc_config_windows.go +++ b/cmd/launcher/svc_config_windows.go @@ -66,7 +66,7 @@ func checkServiceConfiguration(logger *slog.Logger, opts *launcher.Options) { checkRestartActions(logger) - setRecoveryActions(logger) + setRecoveryActions(context.TODO(), logger) } // checkDelayedAutostart checks the current value of `DelayedAutostart` (whether to wait ~2 minutes @@ -190,7 +190,7 @@ func checkRestartActions(logger *slog.Logger) { // setRecoveryActions sets the recovery actions for the launcher service. // previously defined via wix ServicConfig Element (Util Extension) https://wixtoolset.org/docs/v3/xsd/util/serviceconfig/ -func setRecoveryActions(logger *slog.Logger) { +func setRecoveryActions(_ context.Context, logger *slog.Logger) { sman, err := mgr.Connect() if err != nil { logger.Log(context.TODO(), slog.LevelError, diff --git a/cmd/package-builder/package-builder.go b/cmd/package-builder/package-builder.go index effac1b09..6063a8f2a 100644 --- a/cmd/package-builder/package-builder.go +++ b/cmd/package-builder/package-builder.go @@ -79,12 +79,15 @@ func runMake(args []string) error { env.String("LAUNCHER_VERSION", "stable"), "What TUF channel to download launcher from. Supports filesystem paths", ) - // flLauncherArmVersion primarily exists to be able to provide the path to a local launcher arm binary - // for testing - flLauncherArmVersion = flagset.String( - "launcher_arm_version", + flLauncherPath = flagset.String( + "launcher_path", "", - "What TUF channel to download launcher from for ARM. Supports filesystem paths, defaults to value of launcher_version", + "Path of local launcher binary to use in packaging", + ) + flLauncherArmPath = flagset.String( + "launcher_arm_path", + "", + "Path of local launcher arm64 binary to use in packaging", ) flExtensionVersion = flagset.String( "extension_version", @@ -237,35 +240,34 @@ func runMake(args []string) error { } packageOptions := packaging.PackageOptions{ - PackageVersion: *flPackageVersion, - OsqueryVersion: *flOsqueryVersion, - OsqueryFlags: flOsqueryFlags, - LauncherVersion: *flLauncherVersion, - LauncherArmVersion: *flLauncherArmVersion, - ExtensionVersion: *flExtensionVersion, - Hostname: *flHostname, - Secret: *flEnrollSecret, - AppleSigningKey: *flSigningKey, - Transport: *flTransport, - Insecure: *flInsecure, - InsecureTransport: *flInsecureTransport, - UpdateChannel: *flUpdateChannel, - InitialRunner: *flInitialRunner, - Identifier: *flIdentifier, - OmitSecret: *flOmitSecret, - CertPins: *flCertPins, - RootPEM: *flRootPEM, - BinRootDir: *flBinRootDir, - CacheDir: cacheDir, - TufServerURL: *flTufURL, - MirrorURL: *flMirrorURL, - WixPath: *flWixPath, - WixSkipCleanup: *flWixSkipCleanup, - DisableService: *flDisableService, - } - - if packageOptions.LauncherArmVersion == "" { - packageOptions.LauncherArmVersion = packageOptions.LauncherVersion + PackageVersion: *flPackageVersion, + OsqueryVersion: *flOsqueryVersion, + OsqueryFlags: flOsqueryFlags, + LauncherVersion: *flLauncherVersion, + LauncherPath: *flLauncherPath, + // LauncherArmPath can be used for windows arm64 packages when you want + // to specify a local path to the launcher binary + LauncherArmPath: *flLauncherArmPath, + ExtensionVersion: *flExtensionVersion, + Hostname: *flHostname, + Secret: *flEnrollSecret, + AppleSigningKey: *flSigningKey, + Transport: *flTransport, + Insecure: *flInsecure, + InsecureTransport: *flInsecureTransport, + UpdateChannel: *flUpdateChannel, + InitialRunner: *flInitialRunner, + Identifier: *flIdentifier, + OmitSecret: *flOmitSecret, + CertPins: *flCertPins, + RootPEM: *flRootPEM, + BinRootDir: *flBinRootDir, + CacheDir: cacheDir, + TufServerURL: *flTufURL, + MirrorURL: *flMirrorURL, + WixPath: *flWixPath, + WixSkipCleanup: *flWixSkipCleanup, + DisableService: *flDisableService, } outputDir := *flOutputDir diff --git a/pkg/packagekit/wix/wix.go b/pkg/packagekit/wix/wix.go index d92aa890a..bc879dd1c 100644 --- a/pkg/packagekit/wix/wix.go +++ b/pkg/packagekit/wix/wix.go @@ -162,6 +162,12 @@ func New(packageRoot string, identifier string, mainWxsContent []byte, wixOpts . // Cleanup removes temp directories. Meant to be called in a defer. func (wo *wixTool) Cleanup() { if wo.skipCleanup { + + fmt.Print("skipping cleanup of temp directories\n") + for _, d := range wo.cleanDirs { + fmt.Printf("skipping cleanup of %s\n", d) + } + return } diff --git a/pkg/packaging/packaging.go b/pkg/packaging/packaging.go index 44fc08f2d..b7a7982fd 100644 --- a/pkg/packaging/packaging.go +++ b/pkg/packaging/packaging.go @@ -32,32 +32,33 @@ const ( // PackageOptions encapsulates the launcher build options. It's // populated by callers, such as command line flags. It may change. type PackageOptions struct { - PackageVersion string // What version in this package. If unset, autodetection will be attempted. - OsqueryVersion string - OsqueryFlags []string // Additional flags to pass to the runtime osquery instance - LauncherVersion string - LauncherArmVersion string - ExtensionVersion string - Hostname string - Secret string - Transport string - Insecure bool - InsecureTransport bool - UpdateChannel string - InitialRunner bool - Identifier string - Title string - OmitSecret bool - CertPins string - RootPEM string - BinRootDir string - CacheDir string - TufServerURL string - MirrorURL string - WixPath string - MSIUI bool - WixSkipCleanup bool - DisableService bool + PackageVersion string // What version in this package. If unset, autodetection will be attempted. + OsqueryVersion string + OsqueryFlags []string // Additional flags to pass to the runtime osquery instance + LauncherVersion string + LauncherPath string + LauncherArmPath string + ExtensionVersion string + Hostname string + Secret string + Transport string + Insecure bool + InsecureTransport bool + UpdateChannel string + InitialRunner bool + Identifier string + Title string + OmitSecret bool + CertPins string + RootPEM string + BinRootDir string + CacheDir string + TufServerURL string + MirrorURL string + WixPath string + MSIUI bool + WixSkipCleanup bool + DisableService bool AppleNotarizeAccountId string // The 10 character apple account id AppleNotarizeAppPassword string // app password for notarization service @@ -214,7 +215,12 @@ func (p *PackageOptions) Build(ctx context.Context, packageWriter io.Writer, tar return fmt.Errorf("fetching binary osqueryd: %w", err) } - if err := p.getBinary(ctx, "launcher", p.target.PlatformBinaryName("launcher"), p.LauncherVersion); err != nil { + launcherVersion := p.LauncherVersion + if p.LauncherPath != "" { + launcherVersion = p.LauncherPath + } + + if err := p.getBinary(ctx, "launcher", p.target.PlatformBinaryName("launcher"), launcherVersion); err != nil { return fmt.Errorf("fetching binary launcher: %w", err) } @@ -228,7 +234,12 @@ func (p *PackageOptions) Build(ctx context.Context, packageWriter io.Writer, tar return fmt.Errorf("fetching binary osqueryd: %w", err) } - if err := packageOptsCopy.getBinary(ctx, "launcher", packageOptsCopy.target.PlatformBinaryName("launcher"), packageOptsCopy.LauncherArmVersion); err != nil { + launcherVersion := packageOptsCopy.LauncherVersion + if packageOptsCopy.LauncherArmPath != "" { + launcherVersion = packageOptsCopy.LauncherArmPath + } + + if err := packageOptsCopy.getBinary(ctx, "launcher", packageOptsCopy.target.PlatformBinaryName("launcher"), launcherVersion); err != nil { return fmt.Errorf("fetching binary launcher: %w", err) } } From 10314ccacdf55c3c25d37d63934ffe6d779e386f Mon Sep 17 00:00:00 2001 From: James Pickett Date: Tue, 30 Jul 2024 08:17:21 -0700 Subject: [PATCH 08/10] uppercase osq processor condition --- pkg/packagekit/wix/wix.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/packagekit/wix/wix.go b/pkg/packagekit/wix/wix.go index bc879dd1c..625a6b071 100644 --- a/pkg/packagekit/wix/wix.go +++ b/pkg/packagekit/wix/wix.go @@ -307,7 +307,7 @@ func (wo *wixTool) addServices(ctx context.Context) error { } // create a condition based on architecture - heatWrite.WriteString(fmt.Sprintf(` %sROCESSOR_ARCHITECTURE="%s" `, "%P", currentArchSpecificBinDir)) + heatWrite.WriteString(fmt.Sprintf(` %sROCESSOR_ARCHITECTURE="%s" `, "%P", strings.ToUpper(string(currentArchSpecificBinDir)))) heatWrite.WriteString("\n") } } From 163ca6a704f8732670f913df1def687c819c942e Mon Sep 17 00:00:00 2001 From: James Pickett Date: Mon, 5 Aug 2024 14:24:19 -0700 Subject: [PATCH 09/10] re-add UtilServiceConfig for amd --- pkg/packagekit/wix/service.go | 47 +++++++++++++++++++++++++----- pkg/packagekit/wix/service_test.go | 1 + pkg/packagekit/wix/wix.go | 8 +++++ 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/pkg/packagekit/wix/service.go b/pkg/packagekit/wix/service.go index fe452b648..45f8e4900 100644 --- a/pkg/packagekit/wix/service.go +++ b/pkg/packagekit/wix/service.go @@ -62,6 +62,7 @@ type ServiceInstall struct { Start StartType `xml:",attr,omitempty"` Type string `xml:",attr,omitempty"` Vital YesNoType `xml:",attr,omitempty"` // The overall install should fail if this service fails to install + UtilServiceConfig *UtilServiceConfig `xml:",omitempty"` ServiceConfig *ServiceConfig `xml:",omitempty"` ServiceDependency *ServiceDependency `xml:",omitempty"` } @@ -102,6 +103,25 @@ type ServiceConfig struct { OnUninstall YesNoType `xml:",attr,omitempty"` } +// UtilServiceConfig implements +// http://wixtoolset.org/documentation/manual/v3/xsd/util/serviceconfig.html +// This is used to set FailureActions. There are some +// limitations. Notably, reset period is in days here, though the +// underlying `sc.exe` command supports seconds. (See +// https://github.com/wixtoolset/issues/issues/5963) +// +// Docs are a bit confusing. This schema is supported, and should +// work. The non-util ServiceConfig generates unsupported CNDL1150 +// errors. +type UtilServiceConfig struct { + XMLName xml.Name `xml:"http://schemas.microsoft.com/wix/UtilExtension ServiceConfig"` + FirstFailureActionType string `xml:",attr,omitempty"` + SecondFailureActionType string `xml:",attr,omitempty"` + ThirdFailureActionType string `xml:",attr,omitempty"` + RestartServiceDelayInSeconds int `xml:",attr,omitempty"` + ResetPeriodInDays int `xml:",attr,omitempty"` +} + // Service represents a wix service. It provides an interface to both // ServiceInstall and ServiceControl. type Service struct { @@ -180,6 +200,16 @@ func ServiceArgs(args []string) ServiceOpt { // New returns a service func NewService(matchString string, opts ...ServiceOpt) *Service { + // Set some defaults. It's not clear we can reset in under a + // day. See https://github.com/wixtoolset/issues/issues/5963 + utilServiceConfig := &UtilServiceConfig{ + FirstFailureActionType: "restart", + SecondFailureActionType: "restart", + ThirdFailureActionType: "restart", + ResetPeriodInDays: 1, + RestartServiceDelayInSeconds: 5, + } + serviceConfig := &ServiceConfig{ OnInstall: Yes, OnReinstall: Yes, @@ -193,14 +223,15 @@ func NewService(matchString string, opts ...ServiceOpt) *Service { defaultName := cleanServiceName(strings.TrimSuffix(matchString, ".exe") + ".svc") si := &ServiceInstall{ - Name: defaultName, - Id: defaultName, - Account: `[SERVICEACCOUNT]`, // Wix resolves this to `LocalSystem` - Start: StartAuto, - Type: "ownProcess", - ErrorControl: ErrorControlNormal, - Vital: Yes, - ServiceConfig: serviceConfig, + Name: defaultName, + Id: defaultName, + Account: `[SERVICEACCOUNT]`, // Wix resolves this to `LocalSystem` + Start: StartAuto, + Type: "ownProcess", + ErrorControl: ErrorControlNormal, + Vital: Yes, + UtilServiceConfig: utilServiceConfig, + ServiceConfig: serviceConfig, } sc := &ServiceControl{ diff --git a/pkg/packagekit/wix/service_test.go b/pkg/packagekit/wix/service_test.go index d1d0306d6..44bb7a8b0 100644 --- a/pkg/packagekit/wix/service_test.go +++ b/pkg/packagekit/wix/service_test.go @@ -33,6 +33,7 @@ func TestService(t *testing.T) { require.True(t, expectTrue3) expectedXml := ` + ` diff --git a/pkg/packagekit/wix/wix.go b/pkg/packagekit/wix/wix.go index 625a6b071..d9e81876b 100644 --- a/pkg/packagekit/wix/wix.go +++ b/pkg/packagekit/wix/wix.go @@ -289,6 +289,14 @@ func (wo *wixTool) addServices(ctx context.Context) error { service.serviceInstall.Id = serviceId service.serviceInstall.ServiceConfig.Id = serviceId + // unfortunately, the UtilServiceConfig uses the name of the launcher service as a primary key + // since we have multiple services with the same name, we can't have multiple UtilServiceConfigs + // so we are skipping it for arm64 since it's a much smaller portion of our user base. The correct + // UtilServiceConfig will set when launcher starts up. + if currentArchSpecificBinDir == arm64 { + service.serviceInstall.UtilServiceConfig = nil + } + // create a condition based on architecture // have to format in the "%P" in "%PROCESSOR_ARCHITECTURE" heatWrite.WriteString(fmt.Sprintf(` %sROCESSOR_ARCHITECTURE="%s" `, "%P", strings.ToUpper(string(currentArchSpecificBinDir)))) From 8293631c972fd4f94a7b05e92ae37626180ca871 Mon Sep 17 00:00:00 2001 From: James Pickett Date: Tue, 6 Aug 2024 09:28:47 -0700 Subject: [PATCH 10/10] tweaks, feedback, docs --- cmd/launcher/svc_config_windows.go | 8 ++++---- docs/package-builder.md | 24 ++++++++++++++++++++++++ pkg/packagekit/wix/wix.go | 10 +++++++--- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/cmd/launcher/svc_config_windows.go b/cmd/launcher/svc_config_windows.go index 941514468..05ebf7cbf 100644 --- a/cmd/launcher/svc_config_windows.go +++ b/cmd/launcher/svc_config_windows.go @@ -190,10 +190,10 @@ func checkRestartActions(logger *slog.Logger) { // setRecoveryActions sets the recovery actions for the launcher service. // previously defined via wix ServicConfig Element (Util Extension) https://wixtoolset.org/docs/v3/xsd/util/serviceconfig/ -func setRecoveryActions(_ context.Context, logger *slog.Logger) { +func setRecoveryActions(ctx context.Context, logger *slog.Logger) { sman, err := mgr.Connect() if err != nil { - logger.Log(context.TODO(), slog.LevelError, + logger.Log(ctx, slog.LevelError, "connecting to service control manager", "err", err, ) @@ -205,7 +205,7 @@ func setRecoveryActions(_ context.Context, logger *slog.Logger) { launcherService, err := sman.OpenService(launcherServiceName) if err != nil { - logger.Log(context.TODO(), slog.LevelError, + logger.Log(ctx, slog.LevelError, "opening the launcher service from control manager", "err", err, ) @@ -234,7 +234,7 @@ func setRecoveryActions(_ context.Context, logger *slog.Logger) { } if err := launcherService.SetRecoveryActions(recoveryActions, 24*60*60); err != nil { // 24 hours - logger.Log(context.TODO(), slog.LevelError, + logger.Log(ctx, slog.LevelError, "setting RecoveryActions", "err", err, ) diff --git a/docs/package-builder.md b/docs/package-builder.md index 40ba01f02..b56392321 100644 --- a/docs/package-builder.md +++ b/docs/package-builder.md @@ -193,3 +193,27 @@ Note that the windows package will only install as `ALLUSERS`. You may need to use elevated privileges to install it. This will likely be confusing. `msiexec.exe` will either silently fail, or be inscrutable. But `start` will work. + +###### Windows Multi-Archecture MSI + +Package builder creates an MSI containing both amd64 & arm64 binaries. This is done putting both of binaries and their service installation in to the MSI with mutually exclusive conditions based on processor architecture. + +To accomplish we are using heat to harvest files set up like ... + +``` +└───pkg_root + └───Launcher-kolide-k2 + ├───bin + │ ├───amd64 + │ │ launcher.exe + │ │ osqueryd.exe + │ │ + │ └───arm64 + │ launcher.exe + │ osqueryd.exe + │ + └───conf + ... +``` + +then post processing the generated xml to remove the amd64 and arm64 directory tags so that all the exe files are in the same directory in the xml, then add the mutually exclusive tags to the binary elements diff --git a/pkg/packagekit/wix/wix.go b/pkg/packagekit/wix/wix.go index d9e81876b..ec2e66eea 100644 --- a/pkg/packagekit/wix/wix.go +++ b/pkg/packagekit/wix/wix.go @@ -3,6 +3,7 @@ package wix import ( "bytes" "context" + "errors" "fmt" "os" "os/exec" @@ -162,7 +163,10 @@ func New(packageRoot string, identifier string, mainWxsContent []byte, wixOpts . // Cleanup removes temp directories. Meant to be called in a defer. func (wo *wixTool) Cleanup() { if wo.skipCleanup { - + // if the wix_skip_cleanup flag is set, we don't want to clean up the temp directories + // this is useful when debugging wix generation + // print the directories that would be cleaned up so they can be easily found + // and inspected fmt.Print("skipping cleanup of temp directories\n") for _, d := range wo.cleanDirs { fmt.Printf("skipping cleanup of %s\n", d) @@ -250,7 +254,7 @@ func (wo *wixTool) addServices(ctx context.Context) error { continue } - // the directory tag will like like "" + // the directory tag will look like "" // so we just check for the first part of the string if strings.Contains(line, "