From 5658492062bf28ffb670ede15cbd1413bf1182d8 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Tue, 17 Feb 2026 19:41:03 -0500 Subject: Lock upgrade bundles to original bundle's scope. Fixes https://github.com/wixtoolset/issues/issues/9236 --- .../IBootstrapperEngine.cs | 7 +-- src/burn/engine/core.cpp | 2 +- src/burn/engine/core.h | 5 -- src/burn/engine/engine.mc | 9 +++- src/burn/engine/plan.cpp | 57 +++++++++++++++++++++- src/burn/engine/plan.h | 3 ++ src/burn/engine/registration.cpp | 18 ++++--- src/burn/engine/registration.h | 5 +- src/burn/engine/relatedbundle.cpp | 12 +++++ src/burn/test/BurnUnitTest/RelatedBundleTest.cpp | 3 ++ .../AllPuomBundle/AllPuomBundleTestBA.wixproj | 1 + .../AllPuomBundle/AllPuomBundleTestBAv2.wixproj | 8 +++ .../AllPuomBundle/AllPuomBundleWixStdBA.wixproj | 1 + .../AllPuomBundle/Bundle.wxs | 2 +- .../ConfigurableScopeTests.cs | 48 ++++++++++++++++++ .../Bundles/CreateBurnManifestCommand.cs | 1 + .../BundleFixture.cs | 2 +- .../BundlePackageFixture.cs | 41 ++++++++-------- .../DependencyExtensionFixture.cs | 2 +- 19 files changed, 184 insertions(+), 43 deletions(-) create mode 100644 src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/AllPuomBundleTestBAv2.wixproj diff --git a/src/api/burn/WixToolset.BootstrapperApplicationApi/IBootstrapperEngine.cs b/src/api/burn/WixToolset.BootstrapperApplicationApi/IBootstrapperEngine.cs index 9df57cd3..4b1c4b52 100644 --- a/src/api/burn/WixToolset.BootstrapperApplicationApi/IBootstrapperEngine.cs +++ b/src/api/burn/WixToolset.BootstrapperApplicationApi/IBootstrapperEngine.cs @@ -345,23 +345,24 @@ namespace WixToolset.BootstrapperApplicationApi } /// - /// The scope of the bundle when the chain contains per-user-or-machine or per-machone-or-user packages. + /// The scope of the bundle when the chain contains dual-purpose (per-user-or-machine or per-machone-or-user) packages. /// public enum BundleScope { /// /// Let Burn choose the scope. Per-user-or-machine packages will be + /// planned as per-user packages. Per-machine-or-user packages will be /// planned as per-machine packages. /// Default, /// - /// Set per-machine scope for per-user-or-machine packages. + /// Set per-machine scope for dual-purpose packages. /// PerMachine, /// - /// Set per-user scope for per-user-or-machine packages. + /// Set per-user scope for dual-purpose packages. /// PerUser, } diff --git a/src/burn/engine/core.cpp b/src/burn/engine/core.cpp index dc0af8ce..5483e46f 100644 --- a/src/burn/engine/core.cpp +++ b/src/burn/engine/core.cpp @@ -469,7 +469,7 @@ extern "C" HRESULT CorePlan( pEngineState->plan.fDisableRollback = pEngineState->fDisableRollback || BOOTSTRAPPER_ACTION_UNSAFE_UNINSTALL == pEngineState->plan.action; pEngineState->plan.fPlanPackageCacheRollback = BOOTSTRAPPER_REGISTRATION_TYPE_NONE == pEngineState->registration.detectedRegistrationType; - hr = PlanPackagesAndBundleScope(pEngineState->packages.rgPackages, pEngineState->packages.cPackages, pEngineState->plan.plannedScope, pEngineState->registration.scope, pEngineState->command.commandLineScope, pEngineState->registration.detectedScope, &pEngineState->plan.plannedScope, &pEngineState->registration.fPerMachine); + hr = PlanPackagesAndBundleScope(pEngineState->packages.rgPackages, pEngineState->packages.cPackages, pEngineState->registration.sczPrimaryUpgradeCode, pEngineState->registration.relatedBundles.rgRelatedBundles, pEngineState->registration.relatedBundles.cRelatedBundles, pEngineState->plan.plannedScope, pEngineState->registration.scope, pEngineState->command.commandLineScope, pEngineState->registration.detectedScope, &pEngineState->plan.plannedScope, &pEngineState->registration.fPerMachine); ExitOnFailure(hr, "Failed to determine packages and bundle scope."); if (BOOTSTRAPPER_PACKAGE_SCOPE_PER_MACHINE_OR_PER_USER == pEngineState->registration.scope || BOOTSTRAPPER_PACKAGE_SCOPE_PER_USER_OR_PER_MACHINE == pEngineState->registration.scope) diff --git a/src/burn/engine/core.h b/src/burn/engine/core.h index 520fdcd5..2ad9111f 100644 --- a/src/burn/engine/core.h +++ b/src/burn/engine/core.h @@ -235,11 +235,6 @@ HRESULT CoreSerializeEngineState( HRESULT CoreQueryRegistration( __in BURN_ENGINE_STATE* pEngineState ); -//HRESULT CoreDeserializeEngineState( -// __in BURN_ENGINE_STATE* pEngineState, -// __in_bcount(cbBuffer) BYTE* pbBuffer, -// __in SIZE_T cbBuffer -// ); HRESULT CoreDetect( __in BURN_ENGINE_STATE* pEngineState, __in_opt HWND hwndParent diff --git a/src/burn/engine/engine.mc b/src/burn/engine/engine.mc index f7ec0359..edafef97 100644 --- a/src/burn/engine/engine.mc +++ b/src/burn/engine/engine.mc @@ -418,7 +418,14 @@ MessageId=227 Severity=Success SymbolicName=MSG_PLAN_INSTALLED_SCOPE Language=English -Bundle was already installed with scope: %1!hs! +Bundle was already installed with scope: %1!hs!. Scope cannot change during maintenance. +. + +MessageId=228 +Severity=Success +SymbolicName=MSG_PLAN_UPGRADE_SCOPE +Language=English +Upgraded bundle %1!ls! was already installed with scope: %2!hs!. Scope cannot change during upgrade. . MessageId=201 diff --git a/src/burn/engine/plan.cpp b/src/burn/engine/plan.cpp index 889ad68c..2257b28c 100644 --- a/src/burn/engine/plan.cpp +++ b/src/burn/engine/plan.cpp @@ -144,6 +144,12 @@ static BOOL ForceCache( __in BURN_PLAN* pPlan, __in BURN_PACKAGE* pPackage ); +static HRESULT GetUpgradedBundleScope( + __in DWORD cRelatedBundles, + __in BURN_RELATED_BUNDLE* rgRelatedBundles, + __in_z LPCWSTR wzUpgradeCode, + __inout BOOTSTRAPPER_SCOPE* pScope +); // function definitions @@ -823,6 +829,9 @@ LExit: extern "C" HRESULT PlanPackagesAndBundleScope( __in BURN_PACKAGE* rgPackages, __in DWORD cPackages, + __in_z LPCWSTR wzUpgradeCode, + __in BURN_RELATED_BUNDLE* rgRelatedBundles, + __in DWORD cRelatedBundles, __in BOOTSTRAPPER_SCOPE scope, __in BOOTSTRAPPER_PACKAGE_SCOPE authoredScope, __in BOOTSTRAPPER_SCOPE commandLineScope, @@ -855,7 +864,11 @@ extern "C" HRESULT PlanPackagesAndBundleScope( } } - if (BOOTSTRAPPER_SCOPE_DEFAULT != detectedScope) + // If we're upgrading, lock the scope to that of the upgraded bundle. + hr = GetUpgradedBundleScope(cRelatedBundles, rgRelatedBundles, wzUpgradeCode, &scope); + + // If we're in maintenance mode instead, lock the scope to the original scope. + if (S_FALSE == hr && BOOTSTRAPPER_SCOPE_DEFAULT != detectedScope) { scope = detectedScope; @@ -888,6 +901,48 @@ extern "C" HRESULT PlanPackagesAndBundleScope( } +static HRESULT GetUpgradedBundleScope( + __in DWORD cRelatedBundles, + __in BURN_RELATED_BUNDLE* rgRelatedBundles, + __in_z LPCWSTR wzUpgradeCode, + __inout BOOTSTRAPPER_SCOPE* pScope + ) +{ + HRESULT hr = S_OK; + + for (DWORD i = 0; i < cRelatedBundles; ++i) + { + BURN_RELATED_BUNDLE* pRelatedBundle = rgRelatedBundles + i; + + if (BOOTSTRAPPER_RELATION_UPGRADE == pRelatedBundle->detectRelationType) + { + for (DWORD j = 0; j < pRelatedBundle->package.Bundle.cUpgradeCodes; ++j) + { + LPCWSTR wzRelatedUpgradeCode = *(pRelatedBundle->package.Bundle.rgsczUpgradeCodes + j); + + // Is the related bundle's upgrade code the same as ours? + // If so, lock our scope to the "original" bundle's scope. + if (CSTR_EQUAL == ::CompareStringOrdinal(wzRelatedUpgradeCode, -1, wzUpgradeCode, -1, FALSE)) + if (CSTR_EQUAL == ::CompareStringOrdinal(wzRelatedUpgradeCode, -1, wzUpgradeCode, -1, TRUE)) + { + *pScope = pRelatedBundle->detectedScope; + + LogId(REPORT_STANDARD, MSG_PLAN_UPGRADE_SCOPE, pRelatedBundle->package.Bundle.sczBundleCode, LoggingBundleScopeToString(*pScope)); + + ExitFunction(); + } + } + } + } + + // No upgrade codes or none match, which is fine. But note it via S_FALSE + // so we can distinguish the upgrade case from the maintenance case. + hr = S_FALSE; + +LExit: + return hr; +} + static HRESULT PlanPackagesHelper( __in BURN_PACKAGE* rgPackages, __in DWORD cPackages, diff --git a/src/burn/engine/plan.h b/src/burn/engine/plan.h index f9996ac0..09918440 100644 --- a/src/burn/engine/plan.h +++ b/src/burn/engine/plan.h @@ -485,6 +485,9 @@ void PlanDump( HRESULT PlanPackagesAndBundleScope( __in BURN_PACKAGE* rgPackages, __in DWORD cPackages, + __in_z LPCWSTR wzUpgradeCode, + __in BURN_RELATED_BUNDLE* rgRelatedBundles, + __in DWORD cRelatedBundles, __in BOOTSTRAPPER_SCOPE scope, __in BOOTSTRAPPER_PACKAGE_SCOPE authoredScope, __in BOOTSTRAPPER_SCOPE commandLineScope, diff --git a/src/burn/engine/registration.cpp b/src/burn/engine/registration.cpp index e588bef0..7c9ca182 100644 --- a/src/burn/engine/registration.cpp +++ b/src/burn/engine/registration.cpp @@ -29,9 +29,10 @@ const LPCWSTR REGISTRY_BUNDLE_UNINSTALL_STRING = L"UninstallString"; const LPCWSTR REGISTRY_BUNDLE_RESUME_COMMAND_LINE = L"BundleResumeCommandLine"; const LPCWSTR REGISTRY_BUNDLE_VERSION_MAJOR = L"VersionMajor"; const LPCWSTR REGISTRY_BUNDLE_VERSION_MINOR = L"VersionMinor"; -const LPCWSTR REGISTRY_BUNDLE_SCOPE = L"BundleScope"; const LPCWSTR SWIDTAG_FOLDER = L"swidtag"; const LPCWSTR REGISTRY_BUNDLE_VARIABLE_KEY = L"variables"; +const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed"; + // internal function declarations @@ -146,6 +147,10 @@ extern "C" HRESULT RegistrationParseFromXml( hr = XmlGetAttributeEx(pixnRegistrationNode, L"Tag", &pRegistration->sczTag); ExitOnRequiredXmlQueryFailure(hr, "Failed to get @Tag."); + // @PrimaryUpgradeCode + hr = XmlGetAttributeEx(pixnRegistrationNode, L"PrimaryUpgradeCode", &pRegistration->sczPrimaryUpgradeCode); + ExitOnOptionalXmlQueryFailure(hr, fFoundXml, "Failed to get @PrimaryUpgradeCode."); + hr = BundlePackageEngineParseRelatedCodes(pixnBundle, &pRegistration->rgsczDetectCodes, &pRegistration->cDetectCodes, &pRegistration->rgsczUpgradeCodes, &pRegistration->cUpgradeCodes, &pRegistration->rgsczAddonCodes, &pRegistration->cAddonCodes, &pRegistration->rgsczPatchCodes, &pRegistration->cPatchCodes); ExitOnFailure(hr, "Failed to parse related bundles"); @@ -327,7 +332,8 @@ extern "C" void RegistrationUninitialize( { ReleaseStr(pRegistration->sczCode); ReleaseStr(pRegistration->sczTag); - + ReleaseStr(pRegistration->sczPrimaryUpgradeCode); + for (DWORD i = 0; i < pRegistration->cDetectCodes; ++i) { ReleaseStr(pRegistration->rgsczDetectCodes[i]); @@ -666,8 +672,8 @@ extern "C" HRESULT RegistrationSessionBegin( hr = RegWriteStringFormatted(hkRegistration, REGISTRY_BUNDLE_DISPLAY_ICON, L"%s,0", pRegistration->sczCacheExecutablePath); ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_DISPLAY_ICON); - hr = RegWriteNumber(hkRegistration, REGISTRY_BUNDLE_SCOPE, pRegistration->fPerMachine ? BOOTSTRAPPER_SCOPE_PER_MACHINE : BOOTSTRAPPER_SCOPE_PER_USER); - ExitOnFailure(hr, "Failed to write %ls value.", REGISTRY_BUNDLE_SCOPE); + hr = RegWriteNumber(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_SCOPE, pRegistration->fPerMachine ? BOOTSTRAPPER_SCOPE_PER_MACHINE : BOOTSTRAPPER_SCOPE_PER_USER); + ExitOnFailure(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_SCOPE); // update display name hr = UpdateBundleNameRegistration(pRegistration, pVariables, hkRegistration, BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS == registrationType); @@ -1780,8 +1786,8 @@ static HRESULT DetectInstalled( pRegistration->detectedRegistrationType = (1 == dwInstalled) ? BOOTSTRAPPER_REGISTRATION_TYPE_FULL : BOOTSTRAPPER_REGISTRATION_TYPE_INPROGRESS; - hr = RegReadNumber(hkRegistration, REGISTRY_BUNDLE_SCOPE, &dwScope); - ExitOnFailure(hr, "Failed to read registration %ls@%ls.", pRegistration->sczRegistrationKey, REGISTRY_BUNDLE_SCOPE); + hr = RegReadNumber(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_SCOPE, &dwScope); + ExitOnFailure(hr, "Failed to read registration %ls@%ls.", pRegistration->sczRegistrationKey, BURN_REGISTRATION_REGISTRY_BUNDLE_SCOPE); pRegistration->detectedScope = static_cast(dwScope); } diff --git a/src/burn/engine/registration.h b/src/burn/engine/registration.h index f340999a..97c6951b 100644 --- a/src/burn/engine/registration.h +++ b/src/burn/engine/registration.h @@ -21,8 +21,7 @@ const LPCWSTR BURN_REGISTRATION_REGISTRY_ENGINE_VERSION = L"EngineVersion"; const LPCWSTR BURN_REGISTRATION_REGISTRY_ENGINE_PROTOCOL_VERSION = L"EngineProtocolVersion"; const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_PROVIDER_KEY = L"BundleProviderKey"; const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_TAG = L"BundleTag"; - -const LPCWSTR REGISTRY_BUNDLE_INSTALLED = L"Installed"; +const LPCWSTR BURN_REGISTRATION_REGISTRY_BUNDLE_SCOPE = L"BundleScope"; enum BURN_RESUME_MODE { @@ -69,6 +68,7 @@ typedef struct _BURN_RELATED_BUNDLE BOOTSTRAPPER_REQUEST_STATE defaultRequestedRestore; BOOTSTRAPPER_REQUEST_STATE requestedRestore; BOOTSTRAPPER_ACTION_STATE restore; + BOOTSTRAPPER_SCOPE detectedScope; } BURN_RELATED_BUNDLE; typedef struct _BURN_RELATED_BUNDLES @@ -106,6 +106,7 @@ typedef struct _BURN_REGISTRATION BOOTSTRAPPER_PACKAGE_SCOPE scope; LPWSTR sczCode; LPWSTR sczTag; + LPWSTR sczPrimaryUpgradeCode; LPWSTR *rgsczDetectCodes; DWORD cDetectCodes; diff --git a/src/burn/engine/relatedbundle.cpp b/src/burn/engine/relatedbundle.cpp index d0b97af0..166c4ae8 100644 --- a/src/burn/engine/relatedbundle.cpp +++ b/src/burn/engine/relatedbundle.cpp @@ -325,6 +325,7 @@ static HRESULT LoadRelatedBundleFromKey( BOOL fExists = FALSE; BURN_DEPENDENCY_PROVIDER dependencyProvider = { }; BURN_DEPENDENCY_PROVIDER* pBundleDependencyProvider = NULL; + DWORD dwScope = 0; // Only support progress from engines that are compatible. hr = RegReadNumber(hkBundleCode, BURN_REGISTRATION_REGISTRY_ENGINE_PROTOCOL_VERSION, &dwEngineProtocolVersion); @@ -355,6 +356,11 @@ static HRESULT LoadRelatedBundleFromKey( LogId(REPORT_WARNING, MSG_RELATED_PACKAGE_INVALID_VERSION, wzRelatedBundleCode, sczBundleVersion); } + hr = RegReadNumber(hkBundleCode, BURN_REGISTRATION_REGISTRY_BUNDLE_SCOPE, &dwScope); + ExitOnFailure(hr, "Failed to read registration %ls for bundle %ls.", wzRelatedBundleCode, BURN_REGISTRATION_REGISTRY_BUNDLE_SCOPE); + + pRelatedBundle->detectedScope = static_cast(dwScope); + hr = RegReadString(hkBundleCode, BURN_REGISTRATION_REGISTRY_BUNDLE_CACHE_PATH, &sczCachePath); ExitOnFailure(hr, "Failed to read cache path from registry for bundle: %ls", wzRelatedBundleCode); @@ -390,6 +396,12 @@ static HRESULT LoadRelatedBundleFromKey( pRelatedBundle->detectRelationType = relationType; + hr = StrAllocString(&pRelatedBundle->package.Bundle.sczBundleCode, wzRelatedBundleCode, 0); + ExitOnFailure(hr, "Failed to bundle code to related bundle."); + + hr = RegReadStringArray(hkBundleCode, BURN_REGISTRATION_REGISTRY_BUNDLE_UPGRADE_CODE, &pRelatedBundle->package.Bundle.rgsczUpgradeCodes, &pRelatedBundle->package.Bundle.cUpgradeCodes); + ExitOnFailure(hr, "Failed to read upgrade codes."); + hr = PseudoBundleInitializeRelated(&pRelatedBundle->package, fSupportsBurnProtocol, fPerMachine, wzRelatedBundleCode, #ifdef DEBUG pRelatedBundle->detectRelationType, diff --git a/src/burn/test/BurnUnitTest/RelatedBundleTest.cpp b/src/burn/test/BurnUnitTest/RelatedBundleTest.cpp index dcccc589..38c99121 100644 --- a/src/burn/test/BurnUnitTest/RelatedBundleTest.cpp +++ b/src/burn/test/BurnUnitTest/RelatedBundleTest.cpp @@ -178,6 +178,9 @@ namespace Bootstrapper hr = RegWriteString(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION, wzVersion); NativeAssert::Succeeded(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_VERSION); + + hr = RegWriteNumber(hkRegistration, BURN_REGISTRATION_REGISTRY_BUNDLE_SCOPE, fPerMachine ? BOOTSTRAPPER_SCOPE_PER_MACHINE : BOOTSTRAPPER_SCOPE_PER_USER); + NativeAssert::Succeeded(hr, "Failed to write %ls value.", BURN_REGISTRATION_REGISTRY_BUNDLE_SCOPE); } finally { diff --git a/src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/AllPuomBundleTestBA.wixproj b/src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/AllPuomBundleTestBA.wixproj index 30d850d4..258d7b42 100644 --- a/src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/AllPuomBundleTestBA.wixproj +++ b/src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/AllPuomBundleTestBA.wixproj @@ -1,6 +1,7 @@  TestBA + $(DefineConstants);Version=1.0.0.0 diff --git a/src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/AllPuomBundleTestBAv2.wixproj b/src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/AllPuomBundleTestBAv2.wixproj new file mode 100644 index 00000000..0daacca8 --- /dev/null +++ b/src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/AllPuomBundleTestBAv2.wixproj @@ -0,0 +1,8 @@ + + + TestBA + $(DefineConstants);Version=2.0.0.0 + + + + \ No newline at end of file diff --git a/src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/AllPuomBundleWixStdBA.wixproj b/src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/AllPuomBundleWixStdBA.wixproj index 56db1fdc..9ca5f8a3 100644 --- a/src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/AllPuomBundleWixStdBA.wixproj +++ b/src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/AllPuomBundleWixStdBA.wixproj @@ -1,6 +1,7 @@  WixStdBA + $(DefineConstants);Version=1.0.0.0 diff --git a/src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/Bundle.wxs b/src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/Bundle.wxs index 4cb11a98..8e1a881a 100644 --- a/src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/Bundle.wxs +++ b/src/test/burn/TestData/ConfigurableScopeTests/AllPuomBundle/Bundle.wxs @@ -1,5 +1,5 @@  - + diff --git a/src/test/burn/WixToolsetTest.BurnE2E/ConfigurableScopeTests.cs b/src/test/burn/WixToolsetTest.BurnE2E/ConfigurableScopeTests.cs index 8b31ac00..bb1381b3 100644 --- a/src/test/burn/WixToolsetTest.BurnE2E/ConfigurableScopeTests.cs +++ b/src/test/burn/WixToolsetTest.BurnE2E/ConfigurableScopeTests.cs @@ -95,6 +95,54 @@ namespace WixToolsetTest.BurnE2E pkg2.VerifyInstalled(false); } + [RuntimeFact] + public void BundleUpgradeIsLockedToFirstBundlesScope() + { + var testBAController = this.CreateTestBAController(); + testBAController.SetBundleScope(BundleScope.Default); + + var bundle = this.CreateBundleInstaller("AllPuomBundleTestBA"); + var log = bundle.Install(); + + bundle.VerifyRegisteredAndInPackageCache(plannedPerMachine: false); + + Assert.True(LogVerifier.MessageInLogFile(log, "Plan begin, 3 packages, action: Install, planned scope: Default")); + + log = bundle.Repair(); + Assert.True(LogVerifier.MessageInLogFile(log, "Bundle was already installed with scope: PerUser. Scope cannot change during maintenance.")); + + var bundleV2 = this.CreateBundleInstaller("AllPuomBundleTestBAv2"); + testBAController.SetBundleScope(BundleScope.PerMachine); + log = bundleV2.Install(); + Assert.True(LogVerifier.MessageInLogFileRegex(log, @"Upgraded bundle [{][0-9A-Fa-f\-]{36}[}] was already installed with scope: PerUser\. Scope cannot change during upgrade\.")); + + bundleV2.Uninstall(); + bundleV2.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: false); + bundle.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: false); + } + + [RuntimeFact] + public void BundleUpgradeWithSameScopeSucceeds() + { + var bundle = this.CreateBundleInstaller("AllPuomBundleTestBA"); + var log = bundle.Install(); + + bundle.VerifyRegisteredAndInPackageCache(plannedPerMachine: false); + + Assert.True(LogVerifier.MessageInLogFile(log, "Plan begin, 3 packages, action: Install, planned scope: Default")); + + log = bundle.Repair(); + Assert.True(LogVerifier.MessageInLogFile(log, "Bundle was already installed with scope: PerUser. Scope cannot change during maintenance.")); + + var bundleV2 = this.CreateBundleInstaller("AllPuomBundleTestBAv2"); + log = bundleV2.Install(); + Assert.True(LogVerifier.MessageInLogFileRegex(log, @"Upgraded bundle [{][0-9A-Fa-f\-]{36}[}] was already installed with scope: PerUser\. Scope cannot change during upgrade\.")); + + bundleV2.Uninstall(); + bundleV2.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: false); + bundle.VerifyUnregisteredAndRemovedFromPackageCache(plannedPerMachine: false); + } + [RuntimeFact] public void PMOU_Bundle_Default_Plan_Installs_PerMachine() { diff --git a/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs b/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs index a1ca32ac..d7f90f1e 100644 --- a/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs +++ b/src/wix/WixToolset.Core.Burn/Bundles/CreateBurnManifestCommand.cs @@ -227,6 +227,7 @@ namespace WixToolset.Core.Burn.Bundles writer.WriteAttributeString("Tag", this.BundleSymbol.Tag); writer.WriteAttributeString("Version", this.BundleSymbol.Version); writer.WriteAttributeString("ProviderKey", this.BundleSymbol.ProviderKey); + writer.WriteAttributeString("PrimaryUpgradeCode", this.BundleSymbol.UpgradeCode); writer.WriteStartElement("Arp"); writer.WriteAttributeString("DisplayName", this.BundleSymbol.Name); diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs index 93aae7be..ef168f88 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundleFixture.cs @@ -170,7 +170,7 @@ namespace WixToolsetTest.CoreIntegration var registrationElements = extractResult.GetManifestTestXmlLines("/burn:BurnManifest/burn:Registration"); WixAssert.CompareLineByLine(new[] { - $"" + + $"" + "" + "", }, registrationElements); diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs index 51fdac6f..fa787977 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/BundlePackageFixture.cs @@ -50,7 +50,7 @@ namespace WixToolsetTest.CoreIntegration Assert.True(File.Exists(chainBundlePath)); - var chainBundleId = GetBundleCodeFromWixpdb(chainPdbPath); + var (chainBundleId, _) = GetBundleCodesFromWixpdb(chainPdbPath); // parent.exe result = WixRunner.Execute(new[] @@ -68,7 +68,7 @@ namespace WixToolsetTest.CoreIntegration Assert.True(File.Exists(parentBundlePath)); - var parentBundleId = GetBundleCodeFromWixpdb(parentPdbPath); + var (parentBundleId, parentUpgradeCode) = GetBundleCodesFromWixpdb(parentPdbPath); var extractResult = BundleExtractor.ExtractBAContainer(null, parentBundlePath, parentBaFolderPath, extractFolderPath); extractResult.AssertSuccess(); @@ -91,7 +91,7 @@ namespace WixToolsetTest.CoreIntegration var registrations = extractResult.GetManifestTestXmlLines("/burn:BurnManifest/burn:Registration"); WixAssert.CompareLineByLine(new[] { - $"" + + $"" + "" + "" }, registrations); @@ -121,7 +121,7 @@ namespace WixToolsetTest.CoreIntegration Assert.True(File.Exists(grandparentBundlePath)); - var grandparentBundleId = GetBundleCodeFromWixpdb(grandparentPdbPath); + var (grandparentBundleId, grandparentUpgradeCode) = GetBundleCodesFromWixpdb(grandparentPdbPath); var grandparentExtractResult = BundleExtractor.ExtractBAContainer(null, grandparentBundlePath, grandparentBaFolderPath, extractFolderPath); grandparentExtractResult.AssertSuccess(); @@ -154,7 +154,7 @@ namespace WixToolsetTest.CoreIntegration registrations = grandparentExtractResult.GetManifestTestXmlLines("/burn:BurnManifest/burn:Registration"); WixAssert.CompareLineByLine(new[] { - $"" + + $"" + "" + "" }, registrations); @@ -202,7 +202,7 @@ namespace WixToolsetTest.CoreIntegration var chainBundleId = "{216BDA7F-74BD-45E8-957B-500552F05629}"; - var parentBundleId = GetBundleCodeFromWixpdb(parentPdbPath); + var (parentBundleId, parentUpgradeCode) = GetBundleCodesFromWixpdb(parentPdbPath); var extractResult = BundleExtractor.ExtractBAContainer(null, parentBundlePath, parentBaFolderPath, extractFolderPath); extractResult.AssertSuccess(); @@ -224,7 +224,7 @@ namespace WixToolsetTest.CoreIntegration var registrations = extractResult.GetManifestTestXmlLines("/burn:BurnManifest/burn:Registration"); WixAssert.CompareLineByLine(new[] { - $"" + + $"" + "" + "" }, registrations); @@ -280,7 +280,7 @@ namespace WixToolsetTest.CoreIntegration Assert.True(File.Exists(parentBundlePath)); - var parentBundleId = GetBundleCodeFromWixpdb(parentPdbPath); + var (parentBundleId, parentUpgradeCode) = GetBundleCodesFromWixpdb(parentPdbPath); var extractResult = BundleExtractor.ExtractBAContainer(null, parentBundlePath, baFolderPath, extractFolderPath); extractResult.AssertSuccess(); @@ -302,7 +302,7 @@ namespace WixToolsetTest.CoreIntegration var registrations = extractResult.GetManifestTestXmlLines("/burn:BurnManifest/burn:Registration"); WixAssert.CompareLineByLine(new[] { - $"" + + $"" + "" + "" }, registrations); @@ -364,7 +364,7 @@ namespace WixToolsetTest.CoreIntegration result.AssertSuccess(); - var bundleId = GetBundleCodeFromWixpdb(bundlePdbPath); + var (bundleId, _) = GetBundleCodesFromWixpdb(bundlePdbPath); var consumingBundleIntermediateFolder = Path.Combine(baseFolder, "obj", "bundle2"); var consumingBundleBinFolder = Path.Combine(baseFolder, "bin", "bundle2"); @@ -383,7 +383,7 @@ namespace WixToolsetTest.CoreIntegration result.AssertSuccess(); - var bundle2Id = GetBundleCodeFromWixpdb(consumingBundlePdbPath); + var (bundle2Id, bundle2UpgradeCode) = GetBundleCodesFromWixpdb(consumingBundlePdbPath); var parentBaFolderPath = Path.Combine(baseFolder, "parentba"); var grandparentBaFolderPath = Path.Combine(baseFolder, "grandparentba"); @@ -409,7 +409,7 @@ namespace WixToolsetTest.CoreIntegration var registrations = bundle2ExtractResult.GetManifestTestXmlLines("/burn:BurnManifest/burn:Registration"); WixAssert.CompareLineByLine(new[] { - $"" + + $"" + "" + "" }, registrations); @@ -461,7 +461,7 @@ namespace WixToolsetTest.CoreIntegration result.AssertSuccess(); - var parentBundleId = GetBundleCodeFromWixpdb(bundlePdbPath); + var (parentBundleId, parentUpgradeCode) = GetBundleCodesFromWixpdb(bundlePdbPath); var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath); extractResult.AssertSuccess(); @@ -469,7 +469,7 @@ namespace WixToolsetTest.CoreIntegration var registrations = extractResult.GetManifestTestXmlLines("/burn:BurnManifest/burn:Registration"); WixAssert.CompareLineByLine(new[] { - $"" + + $"" + "" + "" }, registrations); @@ -532,7 +532,7 @@ namespace WixToolsetTest.CoreIntegration result.AssertSuccess(); - var parentBundleId = GetBundleCodeFromWixpdb(bundlePdbPath); + var (parentBundleId, parentUpgradeCode) = GetBundleCodesFromWixpdb(bundlePdbPath); var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath); extractResult.AssertSuccess(); @@ -540,7 +540,7 @@ namespace WixToolsetTest.CoreIntegration var registrations = extractResult.GetManifestTestXmlLines("/burn:BurnManifest/burn:Registration"); WixAssert.CompareLineByLine(new[] { - $"" + + $"" + "" + "" }, registrations); @@ -603,7 +603,7 @@ namespace WixToolsetTest.CoreIntegration result.AssertSuccess(); - var parentBundleId = GetBundleCodeFromWixpdb(bundlePdbPath); + var (parentBundleId, parentUpgradeCode) = GetBundleCodesFromWixpdb(bundlePdbPath); var extractResult = BundleExtractor.ExtractBAContainer(null, bundlePath, baFolderPath, extractFolderPath); extractResult.AssertSuccess(); @@ -611,7 +611,7 @@ namespace WixToolsetTest.CoreIntegration var registrations = extractResult.GetManifestTestXmlLines("/burn:BurnManifest/burn:Registration"); WixAssert.CompareLineByLine(new[] { - $"" + + $"" + "" + "" }, registrations); @@ -629,16 +629,15 @@ namespace WixToolsetTest.CoreIntegration } } - private static string GetBundleCodeFromWixpdb(string bundlePdbPath) + private static (string, string) GetBundleCodesFromWixpdb(string bundlePdbPath) { using (var wixOutput = WixOutput.Read(bundlePdbPath)) { - var intermediate = Intermediate.Load(wixOutput); var section = intermediate.Sections.Single(); var bundleSymbol = section.Symbols.OfType().Single(); - return bundleSymbol.BundleCode; + return (bundleSymbol.BundleCode, bundleSymbol.UpgradeCode); } } } diff --git a/src/wix/test/WixToolsetTest.CoreIntegration/DependencyExtensionFixture.cs b/src/wix/test/WixToolsetTest.CoreIntegration/DependencyExtensionFixture.cs index e26fda71..e684b7d9 100644 --- a/src/wix/test/WixToolsetTest.CoreIntegration/DependencyExtensionFixture.cs +++ b/src/wix/test/WixToolsetTest.CoreIntegration/DependencyExtensionFixture.cs @@ -144,7 +144,7 @@ namespace WixToolsetTest.CoreIntegration var registration = extractResult.GetManifestTestXmlLines("/burn:BurnManifest/burn:Registration", ignoreAttributesByElementName); WixAssert.CompareLineByLine(new string[] { - "", + "", }, registration); } } -- cgit v1.2.3-55-g6feb