Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions src/Cli/dotnet/commands/InstallingWorkloadCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ internal abstract class InstallingWorkloadCommand : WorkloadCommandBase
protected readonly ReleaseVersion _targetSdkVersion;
protected readonly string _fromRollbackDefinition;
protected string _workloadSetVersion;
protected string _workloadSetVersionFromGlobalJson;
protected readonly PackageSourceLocation _packageSourceLocation;
protected readonly IWorkloadResolverFactory _workloadResolverFactory;
protected IWorkloadResolver _workloadResolver;
Expand Down Expand Up @@ -104,7 +105,15 @@ public static bool ShouldUseWorkloadSetMode(SdkFeatureBand sdkFeatureBand, strin
return installStateContents.UseWorkloadSets ?? false;
}

protected IEnumerable<ManifestVersionUpdate> HandleWorkloadUpdateFromVersion(ITransactionContext context, DirectoryPath? offlineCache)
protected void ErrorIfGlobalJsonAndCommandLineMismatch(string globaljsonPath)
{
if (!string.IsNullOrWhiteSpace(_workloadSetVersionFromGlobalJson) && !string.IsNullOrWhiteSpace(_workloadSetVersion) && !_workloadSetVersion.Equals(_workloadSetVersionFromGlobalJson))
{
throw new Exception(string.Format(Strings.CannotSpecifyVersionOnCommandLineAndInGlobalJson, globaljsonPath));
}
}

protected bool TryHandleWorkloadUpdateFromVersion(ITransactionContext context, DirectoryPath? offlineCache, out IEnumerable<ManifestVersionUpdate> updates)
{
// Ensure workload set mode is set to 'workloadset'
// Do not skip checking the mode first, as setting it triggers
Expand All @@ -114,27 +123,36 @@ protected IEnumerable<ManifestVersionUpdate> HandleWorkloadUpdateFromVersion(ITr
_workloadInstaller.UpdateInstallMode(_sdkFeatureBand, true);
}

_workloadManifestUpdater.DownloadWorkloadSet(_workloadSetVersion, offlineCache);
return InstallWorkloadSet(context);
_workloadManifestUpdater.DownloadWorkloadSet(_workloadSetVersionFromGlobalJson ?? _workloadSetVersion, offlineCache);
return TryInstallWorkloadSet(context, out updates);
}

public IEnumerable<ManifestVersionUpdate> InstallWorkloadSet(ITransactionContext context)
public bool TryInstallWorkloadSet(ITransactionContext context, out IEnumerable<ManifestVersionUpdate> updates)
{
var advertisingPackagePath = Path.Combine(_userProfileDir, "sdk-advertising", _sdkFeatureBand.ToString(), "microsoft.net.workloads");
if (File.Exists(Path.Combine(advertisingPackagePath, Constants.workloadSetVersionFileName)))
{
// This file isn't created in tests.
PrintWorkloadSetTransition(File.ReadAllText(Path.Combine(advertisingPackagePath, Constants.workloadSetVersionFileName)));
}
else if (_workloadInstaller is FileBasedInstaller || _workloadInstaller is NetSdkMsiInstallerClient)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for checking the types here? Is it because this is supposed to behave differently in tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah; the tests don't write and read real advertising manifests, and that means the file should never exist, but we shouldn't fail because of that.

{
// No workload sets found
Reporter.WriteLine(Update.LocalizableStrings.NoWorkloadUpdateFound);
updates = null;
return false;
}

var workloadSetPath = _workloadInstaller.InstallWorkloadSet(context, advertisingPackagePath);
var files = Directory.EnumerateFiles(workloadSetPath, "*.workloadset.json");
return _workloadManifestUpdater.ParseRollbackDefinitionFiles(files);
updates = _workloadManifestUpdater.ParseRollbackDefinitionFiles(files);
return true;
}

private void PrintWorkloadSetTransition(string newVersion)
{
var currentVersion = _workloadResolver.GetWorkloadVersion();
if (currentVersion == null)
if (currentVersion == null || !string.IsNullOrWhiteSpace(_workloadSetVersionFromGlobalJson))
{
Reporter.WriteLine(string.Format(Strings.NewWorkloadSet, newVersion));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ public void UpdateInstallMode(SdkFeatureBand sdkFeatureBand, bool newMode)
var installStateContents = InstallStateContents.FromPath(path);
installStateContents.UseWorkloadSets = newMode;
File.WriteAllText(path, installStateContents.ToString());
_reporter.WriteLine(string.Format(LocalizableStrings.UpdatedWorkloadMode, newMode ? WorkloadConfigCommandParser.UpdateMode_WorkloadSet : WorkloadConfigCommandParser.UpdateMode_Manifests));
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -352,4 +352,10 @@
<data name="WorkloadSetUpgrade" xml:space="preserve">
<value>Updating workload version from {0} to {1}.</value>
</data>
<data name="CannotSpecifyVersionOnCommandLineAndInGlobalJson" xml:space="preserve">
<value>Cannot specify a particular workload version on the command line via --version or --from-history when there is already a version specified in global.json file {0}. To update the globally installed workload version, run the command outside of the path containing that global.json file or update the version specified in the global.json file and run "dotnet workload update."</value>
</data>
<data name="UpdatedWorkloadMode" xml:space="preserve">
<value>Successfully updated workload install mode to use {0}.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ public void InstallWorkloadManifest(ManifestVersionUpdate manifestUpdate, ITrans
},
rollback: () =>
{
InstallWorkloadManifestImplementation(manifestUpdate.Reverse(), offlineCache: null, isRollback: true);
InstallWorkloadManifestImplementation(manifestUpdate, offlineCache: null, isRollback: true, action: InstallAction.Uninstall);
});
}
catch (Exception e)
Expand All @@ -472,7 +472,7 @@ public void InstallWorkloadManifest(ManifestVersionUpdate manifestUpdate, ITrans
}
}

void InstallWorkloadManifestImplementation(ManifestVersionUpdate manifestUpdate, DirectoryPath? offlineCache = null, bool isRollback = false)
void InstallWorkloadManifestImplementation(ManifestVersionUpdate manifestUpdate, DirectoryPath? offlineCache = null, bool isRollback = false, InstallAction action = InstallAction.Install)
{
ReportPendingReboot();

Expand All @@ -491,7 +491,7 @@ void InstallWorkloadManifestImplementation(ManifestVersionUpdate manifestUpdate,
MsiPayload msi = GetCachedMsiPayload(msiPackageId, msiPackageVersion, offlineCache);
VerifyPackage(msi);
DetectState state = DetectPackage(msi.ProductCode, out Version installedVersion);
InstallAction plannedAction = PlanPackage(msi, state, InstallAction.Install, installedVersion);
InstallAction plannedAction = PlanPackage(msi, state, action, installedVersion);

ExecutePackage(msi, plannedAction, msiPackageId);

Expand Down Expand Up @@ -1081,6 +1081,10 @@ private void OnProcessExit(object sender, EventArgs e)
}
}

void IInstaller.UpdateInstallMode(SdkFeatureBand sdkFeatureBand, bool newMode) => UpdateInstallMode(sdkFeatureBand, newMode);
void IInstaller.UpdateInstallMode(SdkFeatureBand sdkFeatureBand, bool newMode)
{
UpdateInstallMode(sdkFeatureBand, newMode);
Reporter.WriteLine(string.Format(LocalizableStrings.UpdatedWorkloadMode, newMode ? WorkloadConfigCommandParser.UpdateMode_WorkloadSet : WorkloadConfigCommandParser.UpdateMode_Manifests));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,12 @@ void GarbageCollectWorkloadSets()
var resolver = GetResolver();

var installedWorkloadSets = resolver.GetWorkloadManifestProvider().GetAvailableWorkloadSets();


foreach (var set in installedWorkloadSets.Keys)
{
WorkloadSetsToKeep.Add(set);
}

var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetDir), "default.json");
if (File.Exists(installStateFilePath))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using Microsoft.DotNet.Cli.NuGetPackageDownloader;
using Microsoft.Extensions.EnvironmentAbstractions;
using static Microsoft.NET.Sdk.WorkloadManifestReader.WorkloadResolver;
using System.Text;

namespace Microsoft.DotNet.Workloads.Workload.Install
{
Expand Down Expand Up @@ -44,14 +45,6 @@ public WorkloadInstallCommand(
_workloadInstaller.GetWorkloadInstallationRecordRepository(), _workloadInstaller, _packageSourceLocation, displayManifestUpdates: Verbosity.IsDetailedOrDiagnostic());

_workloadSetVersion = parseResult.GetValue(InstallingWorkloadCommandParser.WorkloadSetVersionOption);
if (string.IsNullOrWhiteSpace(_workloadSetVersion))
{
// If the version of the workload set is currently pinned, treat it as if it were freshly pinned.
var installStateContents = InstallStateContents.FromPath(Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetPath), "default.json"));
_workloadSetVersion = installStateContents.WorkloadVersion;
}

ValidateWorkloadIdsInput();
}

private void ValidateWorkloadIdsInput()
Expand All @@ -74,6 +67,8 @@ public override int Execute()
bool usedRollback = !string.IsNullOrWhiteSpace(_fromRollbackDefinition);
if (_printDownloadLinkOnly)
{
ValidateWorkloadIdsInput();

Reporter.WriteLine(string.Format(LocalizableStrings.ResolvingPackageUrls, string.Join(", ", _workloadIds)));

// Take the union of the currently installed workloads and the ones that are being requested. This is so that if there are updates to the manifests
Expand All @@ -90,6 +85,8 @@ public override int Execute()
}
else if (!string.IsNullOrWhiteSpace(_downloadToCacheOption))
{
ValidateWorkloadIdsInput();

try
{
// Take the union of the currently installed workloads and the ones that are being requested. This is so that if there are updates to the manifests
Expand All @@ -113,11 +110,34 @@ public override int Execute()
}
else
{
var globaljsonPath = SdkDirectoryWorkloadManifestProvider.GetGlobalJsonPath(Environment.CurrentDirectory);
_workloadSetVersionFromGlobalJson = SdkDirectoryWorkloadManifestProvider.GlobalJsonReader.GetWorkloadVersionFromGlobalJson(globaljsonPath);

try
{
ErrorIfGlobalJsonAndCommandLineMismatch(globaljsonPath);

// Normally we want to validate that the workload IDs specified were valid. However, if there is a global.json file with a workload
// set version specified, and we might update the workload version, then we don't do that check here, because we might not have the right
// workload set installed yet, and trying to list the available workloads would throw an error
if (_skipManifestUpdate || string.IsNullOrEmpty(_workloadSetVersionFromGlobalJson))
{
ValidateWorkloadIdsInput();
}

if (string.IsNullOrWhiteSpace(_workloadSetVersion) && string.IsNullOrWhiteSpace(_workloadSetVersionFromGlobalJson))
{
var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetPath), "default.json");
if (File.Exists(installStateFilePath))
{
var installStateContents = InstallStateContents.FromPath(installStateFilePath);
_workloadSetVersion = installStateContents.WorkloadVersion;
}
}

DirectoryPath? offlineCache = string.IsNullOrWhiteSpace(_fromCacheOption) ? null : new DirectoryPath(_fromCacheOption);
var workloadIds = _workloadIds.Select(id => new WorkloadId(id));
if (string.IsNullOrWhiteSpace(_workloadSetVersion))
if (string.IsNullOrWhiteSpace(_workloadSetVersion) && string.IsNullOrWhiteSpace(_workloadSetVersionFromGlobalJson))
{
InstallWorkloads(
workloadIds,
Expand All @@ -129,9 +149,18 @@ public override int Execute()
{
RunInNewTransaction(context =>
{
var manifests = HandleWorkloadUpdateFromVersion(context, offlineCache);
InstallWorkloadsAndGarbageCollect(context, workloadIds, manifests, offlineCache, false);
if (!TryHandleWorkloadUpdateFromVersion(context, offlineCache, out var manifests))
{
return;
}
InstallWorkloadsWithInstallRecord(context, _workloadInstaller, workloadIds, _sdkFeatureBand, manifests, offlineCache, false);
});

TryRunGarbageCollection(_workloadInstaller, Reporter, Verbosity, workloadSetVersion => _workloadResolverFactory.CreateForWorkloadSet(_dotnetPath, _sdkVersion.ToString(), _userProfileDir, workloadSetVersion), offlineCache);

Reporter.WriteLine();
Reporter.WriteLine(string.Format(LocalizableStrings.InstallationSucceeded, string.Join(" ", workloadIds)));
Reporter.WriteLine();
}
}
catch (Exception e)
Expand All @@ -157,7 +186,9 @@ public void InstallWorkloads(IEnumerable<WorkloadId> workloadIds, bool skipManif
if (!skipManifestUpdate)
{
var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetPath), "default.json");
if (string.IsNullOrWhiteSpace(_fromRollbackDefinition) && File.Exists(installStateFilePath) && InstallStateContents.FromString(File.ReadAllText(installStateFilePath)).Manifests is not null)
var installState = InstallStateContents.FromPath(installStateFilePath);
if (string.IsNullOrWhiteSpace(_fromRollbackDefinition) && string.IsNullOrWhiteSpace(_workloadSetVersion) && string.IsNullOrWhiteSpace(_workloadSetVersionFromGlobalJson) &&
(installState?.Manifests is not null || installState?.WorkloadVersion is not null))
{
// If there is a rollback state file, then we don't want to automatically update workloads when a workload is installed
// To update to a new version, the user would need to run "dotnet workload update"
Expand Down Expand Up @@ -189,7 +220,10 @@ public void InstallWorkloads(IEnumerable<WorkloadId> workloadIds, bool skipManif

if (useWorkloadSets)
{
manifestsToUpdate = InstallWorkloadSet(context);
if (!TryInstallWorkloadSet(context, out manifestsToUpdate))
{
return;
}
}
else
{
Expand All @@ -198,13 +232,8 @@ public void InstallWorkloads(IEnumerable<WorkloadId> workloadIds, bool skipManif
}
}

InstallWorkloadsAndGarbageCollect(context, workloadIds, manifestsToUpdate, offlineCache, useRollback);
InstallWorkloadsWithInstallRecord(context, _workloadInstaller, workloadIds, _sdkFeatureBand, manifestsToUpdate, offlineCache, useRollback);
});
}

private void InstallWorkloadsAndGarbageCollect(ITransactionContext context, IEnumerable<WorkloadId> workloadIds, IEnumerable<ManifestVersionUpdate> manifestsToUpdate, DirectoryPath? offlineCache, bool useRollback)
{
InstallWorkloadsWithInstallRecord(context, _workloadInstaller, workloadIds, _sdkFeatureBand, manifestsToUpdate, offlineCache, useRollback);

TryRunGarbageCollection(_workloadInstaller, Reporter, Verbosity, workloadSetVersion => _workloadResolverFactory.CreateForWorkloadSet(_dotnetPath, _sdkVersion.ToString(), _userProfileDir, workloadSetVersion), offlineCache);

Expand Down Expand Up @@ -265,7 +294,10 @@ private void InstallWorkloadsWithInstallRecord(
installer.SaveInstallStateManifestVersions(sdkFeatureBand, GetInstallStateContents(manifestsToUpdate));
}

installer.AdjustWorkloadSetInInstallState(sdkFeatureBand, string.IsNullOrWhiteSpace(_workloadSetVersion) ? null : _workloadSetVersion);
if (string.IsNullOrWhiteSpace(_workloadSetVersionFromGlobalJson))
{
installer.AdjustWorkloadSetInInstallState(sdkFeatureBand, string.IsNullOrWhiteSpace(_workloadSetVersion) ? null : _workloadSetVersion);
}

_workloadResolver.RefreshWorkloadManifests();

Expand Down
Loading