diff --git a/.azure/pipelines/build-bravo.yaml b/.azure/pipelines/build-bravo.yaml index 89f3e83a..20b569b9 100644 --- a/.azure/pipelines/build-bravo.yaml +++ b/.azure/pipelines/build-bravo.yaml @@ -73,13 +73,13 @@ steps: - task: CmdLine@2 displayName: 'WiX compile app' inputs: - script: '"%WIX%bin\candle.exe" Bravo.wxs -dPublishFolder="$(Build.BinariesDirectory)" -dVersion="$(NBGV_SimpleVersion)" -arch "$(arch)"' + script: '"%WIX%bin\candle.exe" Bravo.wxs -dPublishFolder="$(Build.BinariesDirectory)" -dVersion="$(NBGV_SemVer2)" -dSelfContained="$(selfcontained)" -arch "$(arch)"' workingDirectory: '$(installerSourcesDirectory)' failOnStderr: true - task: CmdLine@2 displayName: 'WiX compile app (perUser)' inputs: - script: '"%WIX%bin\candle.exe" Bravo-perUser.wxs -dPublishFolder="$(Build.BinariesDirectory)" -dVersion="$(NBGV_SimpleVersion)" -arch "$(arch)"' + script: '"%WIX%bin\candle.exe" Bravo-perUser.wxs -dPublishFolder="$(Build.BinariesDirectory)" -dVersion="$(NBGV_SemVer2)" -dSelfContained="$(selfcontained)" -arch "$(arch)"' workingDirectory: '$(installerSourcesDirectory)' failOnStderr: true - task: CmdLine@2 diff --git a/docs/design/versioning.md b/docs/design/versioning.md index 4fb611af..595ddb33 100644 --- a/docs/design/versioning.md +++ b/docs/design/versioning.md @@ -22,18 +22,18 @@ Do not set `Version`, `FileVersion` or `InformationalVersion` in `Bravo.csproj`: | `AssemblyFileVersion` | `X.Y.Z.{height}` | ordering two builds; MSI `ProductVersion` | | `AssemblyInformationalVersion` | `X.Y.Z.{height}[-tag]+{commit}` | diagnostics | | `AssemblyVersion` | `X.Y.0.0` | assembly identity (`assemblyVersion.precision: minor`) | -| `NBGV_SimpleVersion` | `X.Y.Z` | WiX `-dVersion` | -| `NBGV_SemVer2` | `X.Y.Z[-tag]` | artifact names, git tag, `AppVersion.SemanticVersion` | +| `NBGV_SemVer2` | `X.Y.Z[-tag]` | artifact names, git tag, `AppVersion.SemanticVersion`, WiX `-dVersion` | `{height}` is the number of commits since the numeric `X.Y.Z` last changed. It is a build counter: it makes every build uniquely identifiable and orders builds that share the same `X.Y.Z`. ## Rules -- **The prerelease tag never reaches a numeric field.** `AssemblyFileVersion` and `NBGV_SimpleVersion` stay -numeric in every state, so Windows Installer and `System.Version` keep working unchanged. +- **The prerelease tag never reaches a numeric field.** `AssemblyFileVersion` stays numeric in every state, so +Windows Installer and `System.Version` keep working unchanged. WiX `-dVersion` carries the tag but feeds the +installer telemetry only: the MSI `ProductVersion` is bound to the file version of `Bravo.exe`. -- **`SemVer2` is the version that the application and the artifacts report.** The +- **`SemVer2` is the version that the application, the installer and the artifacts report.** The application reads it as `ThisAssembly.NuGetPackageVersion`, which equals `SemVer2` only while `nuGetPackageVersion.semVer` is `2` in `version.json` and `NBGV_ThisAssemblyIncludesPackageVersion` is set in `Bravo.csproj`. Without the first, the application reports the SemVer1 form `X.Y.Z-tag-0001-g{commit}`; without diff --git a/installer/wix/extensions/Bravo.Installer.Wix/Helpers.cs b/installer/wix/extensions/Bravo.Installer.Wix/Helpers.cs index 3c399663..624225af 100644 --- a/installer/wix/extensions/Bravo.Installer.Wix/Helpers.cs +++ b/installer/wix/extensions/Bravo.Installer.Wix/Helpers.cs @@ -15,6 +15,7 @@ internal static class Helpers internal const string PropertyProductName = "PRODUCTNAME"; internal const string PropertyProductVersion = "PRODUCTVERSION"; internal const string PropertyProductBuild = "PRODUCTBUILD"; + internal const string PropertySelfContained = "SELFCONTAINED"; internal const string PropertyProductExecutablePath = "PRODUCTEXECUTABLEPATH"; internal const string PropertyInstallerTelemetryEnabled = "INSTALLERTELEMETRYENABLED"; internal const string PropertyTelemetryUserId = "TELEMETRYUSERID"; @@ -68,18 +69,20 @@ internal static TelemetryClient GetTelemetryClient(Session session) var productVersion = session.CustomActionData[PropertyProductVersion]; var productBuild = session.CustomActionData[PropertyProductBuild]; var userId = session.CustomActionData[PropertyTelemetryUserId]; - var installScope = session.CustomActionData[PropertyInstallScope]; + var installScope = GetInstallScope(session.CustomActionData[PropertyInstallScope]); + var publishMode = GetPublishMode(session.CustomActionData[PropertySelfContained]); var telemetryConfiguration = TelemetryConfiguration.CreateDefault(); telemetryConfiguration.InstrumentationKey = "47a8970c-6293-408a-9cce-5b7b311574d3"; telemetryConfiguration.DisableTelemetry = false; - // Keep telemetry context configuration synchronized with Sqlbi.Bravo.Infrastructure.Helpers.ContextTelemetryInitializer + // Keep telemetry context configuration synchronized with Sqlbi.Bravo.Infrastructure.Telemetry.TelemetrySessionInfo var telemetryClient = new TelemetryClient(telemetryConfiguration); telemetryClient.Context.Device.OperatingSystem = Environment.OSVersion.ToString(); telemetryClient.Context.Component.Version = productVersion; telemetryClient.Context.Session.Id = Guid.NewGuid().ToString(); telemetryClient.Context.User.Id = userId; + telemetryClient.Context.GlobalProperties.Add("PublishMode", publishMode); telemetryClient.Context.GlobalProperties.Add("InstallScope", installScope); telemetryClient.Context.GlobalProperties.Add("ProductName", productName); telemetryClient.Context.GlobalProperties.Add("Version", productVersion); @@ -88,6 +91,40 @@ internal static TelemetryClient GetTelemetryClient(Session session) return telemetryClient; } + /// + /// Maps the self-contained build flag to the publish mode reported by the application. + /// + internal static string GetPublishMode(string selfContained) + { + // This method must not throw because it can be called while reporting exceptions from + // custom actions. An exception here would fail the custom action and roll back the installation. + if (bool.TryParse(selfContained, out var value)) + return value ? "SelfContained" : "FrameworkDependent"; + + // Unexpected values are reported as received, so that the telemetry shows what the build passed in + return selfContained; + } + + /// + /// Maps the WiX Package/@InstallScope value to the deployment mode reported by the application. + /// + internal static string GetInstallScope(string installScope) + { + // The installer telemetry used to send the raw WiX values 'perMachine' and 'perUser'. The values are now + // mapped to 'PerMachine' and 'PerUser', the AppDeploymentMode names sent by the application telemetry, + // so that the InstallScope property has the same set of values for both sources. + // + // See GetPublishMode for why this method must not throw. + if (string.Equals(installScope, "perMachine", StringComparison.OrdinalIgnoreCase)) + return "PerMachine"; + + if (string.Equals(installScope, "perUser", StringComparison.OrdinalIgnoreCase)) + return "PerUser"; + + // Unexpected values are reported as received, so that the telemetry shows what the build passed in + return installScope; + } + internal static bool IsTelemetryEnabled(Session session) { if (session.CustomActionData.TryGetValue(PropertyInstallerTelemetryEnabled, out var value)) diff --git a/installer/wix/src/Bravo/Bravo-perUser.wxs b/installer/wix/src/Bravo/Bravo-perUser.wxs index d6dbbb65..d2617903 100644 --- a/installer/wix/src/Bravo/Bravo-perUser.wxs +++ b/installer/wix/src/Bravo/Bravo-perUser.wxs @@ -11,6 +11,10 @@ + + + + @@ -108,11 +112,11 @@ "1"]]> - + - + - + diff --git a/installer/wix/src/Bravo/Bravo.wxs b/installer/wix/src/Bravo/Bravo.wxs index fcb2a820..fe0add27 100644 --- a/installer/wix/src/Bravo/Bravo.wxs +++ b/installer/wix/src/Bravo/Bravo.wxs @@ -11,7 +11,11 @@ - + + + + + @@ -108,15 +112,15 @@ "1"]]> - + - + - + - + - + diff --git a/installer/wix/src/assets/Bravo.Installer.Wix.CA.dll b/installer/wix/src/assets/Bravo.Installer.Wix.CA.dll index 32a2418a..477dbd83 100644 Binary files a/installer/wix/src/assets/Bravo.Installer.Wix.CA.dll and b/installer/wix/src/assets/Bravo.Installer.Wix.CA.dll differ diff --git a/publish.cmd b/publish.cmd index f19f8c6e..20b8b5b5 100644 --- a/publish.cmd +++ b/publish.cmd @@ -46,8 +46,8 @@ IF EXIST *.wixobj DEL *.wixobj IF EXIST *.wixpdb DEL *.wixpdb %wixheat% dir "%publishfolder%" -gg -scom -srd -sreg -sfrag -templatefragment -cg ComponentsAutogenerated -dr INSTALLFOLDER -var var.PublishFolder -t Bravo.xslt -out Components.wxs -nologo || GOTO :error %wixcandle% Components.wxs -dPublishFolder="%publishfolder%" -arch "%arch%" -nologo || GOTO :error -%wixcandle% Bravo.wxs -arch "%arch%" -dPublishFolder="%publishfolder%" -dVersion="%version%" -nologo || GOTO :error -%wixcandle% Bravo-perUser.wxs -arch "%arch%" -dPublishFolder="%publishfolder%" -dVersion="%version%" -nologo || GOTO :error +%wixcandle% Bravo.wxs -arch "%arch%" -dPublishFolder="%publishfolder%" -dVersion="%version%" -dSelfContained="%selfcontained%" -nologo || GOTO :error +%wixcandle% Bravo-perUser.wxs -arch "%arch%" -dPublishFolder="%publishfolder%" -dVersion="%version%" -dSelfContained="%selfcontained%" -nologo || GOTO :error %wixlight% Bravo.wixobj Components.wixobj -ext WixUIExtension.dll -ext WixUtilExtension.dll -cultures:en-us -loc Bravo-en-us.wxl -out "Bravo-%arch%-en-us.msi" -sice:ICE03 -sice:ICE60 -sice:ICE61 -sice:ICE80 -nologo || GOTO :error %wixlight% Bravo-perUser.wixobj Components.wixobj -ext WixUIExtension.dll -ext WixUtilExtension.dll -cultures:en-us -loc Bravo-en-us.wxl -out "Bravo-%arch%-en-us-userinstaller.msi" -sice:ICE57 -sice:ICE60 -sice:ICE61 -sice:ICE80 -nologo || GOTO :error