Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Permit workload restore to work with new global.json #42606

Merged
merged 8 commits into from
Aug 28, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
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
2 changes: 1 addition & 1 deletion src/Cli/dotnet/commands/InstallingWorkloadCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ protected void UpdateWorkloadManifests(WorkloadHistoryRecorder recorder, ITransa
_workloadInstaller.UpdateInstallMode(_sdkFeatureBand, true);
}

if (SpecifiedWorkloadSetVersionInGlobalJson)
if (SpecifiedWorkloadSetVersionInGlobalJson && recorder is not null)
{
recorder.HistoryRecord.GlobalJsonVersion = _workloadSetVersionFromGlobalJson;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,90 +127,21 @@ public override int Execute()
else
{
WorkloadHistoryRecorder recorder = new WorkloadHistoryRecorder(_workloadResolver, _workloadInstaller, () => _workloadResolverFactory.CreateForWorkloadSet(_dotnetPath, _sdkVersion.ToString(), _userProfileDir, null));
recorder.HistoryRecord.CommandName = IsRunningRestore ? "restore" : "install";
recorder.HistoryRecord.CommandName = "install";

try
{
recorder.Run(() =>
if (!IsRunningRestore)
{
// 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 install that 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))
recorder.Run(() =>
{
ValidateWorkloadIdsInput();
}

Reporter.WriteLine();

DirectoryPath? offlineCache = string.IsNullOrWhiteSpace(_fromCacheOption) ? null : new DirectoryPath(_fromCacheOption);

if (!_skipManifestUpdate)
{
var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _workloadRootDir), "default.json");
if (string.IsNullOrWhiteSpace(_fromRollbackDefinition) &&
!SpecifiedWorkloadSetVersionOnCommandLine &&
!SpecifiedWorkloadSetVersionInGlobalJson &&
InstallStateContents.FromPath(installStateFilePath) is InstallStateContents installState &&
(installState.Manifests != null || installState.WorkloadVersion != null))
{
// If the workload version is pinned in the install state, 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"
_skipManifestUpdate = true;
}
}

RunInNewTransaction(context =>
{
if (!_skipManifestUpdate)
{
if (Verbosity != VerbosityOptions.quiet && Verbosity != VerbosityOptions.q)
{
Reporter.WriteLine(LocalizableStrings.CheckForUpdatedWorkloadManifests);
}
UpdateWorkloadManifests(recorder, context, offlineCache);
}

// Add workload Ids that already exist to our collection to later trigger an update in those installed workloads
var workloadIds = _workloadIds.Select(id => new WorkloadId(id));
var installedWorkloads = _workloadInstaller.GetWorkloadInstallationRecordRepository().GetInstalledWorkloads(_sdkFeatureBand);
var previouslyInstalledWorkloads = installedWorkloads.Intersect(workloadIds);
if (previouslyInstalledWorkloads.Any())
{
Reporter.WriteLine(string.Format(LocalizableStrings.WorkloadAlreadyInstalled, string.Join(" ", previouslyInstalledWorkloads)).Yellow());
}
workloadIds = workloadIds.Concat(installedWorkloads).Distinct();
workloadIds = WriteSDKInstallRecordsForVSWorkloads(workloadIds);

_workloadInstaller.InstallWorkloads(workloadIds, _sdkFeatureBand, context, offlineCache);

// Write workload installation records
var recordRepo = _workloadInstaller.GetWorkloadInstallationRecordRepository();
var newWorkloadInstallRecords = workloadIds.Except(recordRepo.GetInstalledWorkloads(_sdkFeatureBand));
context.Run(
action: () =>
{
foreach (var workloadId in newWorkloadInstallRecords)
{
recordRepo.WriteWorkloadInstallationRecord(workloadId, _sdkFeatureBand);
}
},
rollback: () =>
{
foreach (var workloadId in newWorkloadInstallRecords)
{
recordRepo.DeleteWorkloadInstallationRecord(workloadId, _sdkFeatureBand);
}
});

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

Reporter.WriteLine();
Reporter.WriteLine(string.Format(LocalizableStrings.InstallationSucceeded, string.Join(" ", newWorkloadInstallRecords)));
Reporter.WriteLine();

InstallWorkloads(recorder);
});
});
}
else
{
InstallWorkloads(null);
}
}
catch (Exception e)
{
Expand All @@ -225,6 +156,87 @@ public override int Execute()
return _workloadInstaller.ExitCode;
}

private void InstallWorkloads(WorkloadHistoryRecorder recorder)
{
// 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 install that 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();
}

Reporter.WriteLine();

DirectoryPath? offlineCache = string.IsNullOrWhiteSpace(_fromCacheOption) ? null : new DirectoryPath(_fromCacheOption);

if (!_skipManifestUpdate)
{
var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _workloadRootDir), "default.json");
if (string.IsNullOrWhiteSpace(_fromRollbackDefinition) &&
!SpecifiedWorkloadSetVersionOnCommandLine &&
!SpecifiedWorkloadSetVersionInGlobalJson &&
InstallStateContents.FromPath(installStateFilePath) is InstallStateContents installState &&
(installState.Manifests != null || installState.WorkloadVersion != null))
{
// If the workload version is pinned in the install state, 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"
_skipManifestUpdate = true;
}
}

RunInNewTransaction(context =>
{
if (!_skipManifestUpdate)
{
if (Verbosity != VerbosityOptions.quiet && Verbosity != VerbosityOptions.q)
{
Reporter.WriteLine(LocalizableStrings.CheckForUpdatedWorkloadManifests);
}
UpdateWorkloadManifests(recorder, context, offlineCache);
}

// Add workload Ids that already exist to our collection to later trigger an update in those installed workloads
var workloadIds = _workloadIds.Select(id => new WorkloadId(id));
var installedWorkloads = _workloadInstaller.GetWorkloadInstallationRecordRepository().GetInstalledWorkloads(_sdkFeatureBand);
var previouslyInstalledWorkloads = installedWorkloads.Intersect(workloadIds);
if (previouslyInstalledWorkloads.Any())
{
Reporter.WriteLine(string.Format(LocalizableStrings.WorkloadAlreadyInstalled, string.Join(" ", previouslyInstalledWorkloads)).Yellow());
}
workloadIds = workloadIds.Concat(installedWorkloads).Distinct();
workloadIds = WriteSDKInstallRecordsForVSWorkloads(workloadIds);

_workloadInstaller.InstallWorkloads(workloadIds, _sdkFeatureBand, context, offlineCache);

// Write workload installation records
var recordRepo = _workloadInstaller.GetWorkloadInstallationRecordRepository();
var newWorkloadInstallRecords = workloadIds.Except(recordRepo.GetInstalledWorkloads(_sdkFeatureBand));
context.Run(
action: () =>
{
foreach (var workloadId in newWorkloadInstallRecords)
{
recordRepo.WriteWorkloadInstallationRecord(workloadId, _sdkFeatureBand);
}
},
rollback: () =>
{
foreach (var workloadId in newWorkloadInstallRecords)
{
recordRepo.DeleteWorkloadInstallationRecord(workloadId, _sdkFeatureBand);
}
});

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

Reporter.WriteLine();
Reporter.WriteLine(string.Format(LocalizableStrings.InstallationSucceeded, string.Join(" ", newWorkloadInstallRecords)));
Reporter.WriteLine();

});
}

internal static void TryRunGarbageCollection(IInstaller workloadInstaller, IReporter reporter, VerbosityOptions verbosity, Func<string, IWorkloadResolver> getResolverForWorkloadSet, DirectoryPath? offlineCache = null)
{
try
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
using Microsoft.Build.Execution;
using Microsoft.Build.Logging;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.NuGetPackageDownloader;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Workloads.Workload.Install;
using Microsoft.DotNet.Workloads.Workload.Update;
using Microsoft.Extensions.EnvironmentAbstractions;
using Microsoft.NET.Sdk.WorkloadManifestReader;

Expand All @@ -31,15 +33,46 @@ public WorkloadRestoreCommand(

public override int Execute()
{
var allProjects = DiscoverAllProjects(Directory.GetCurrentDirectory(), _slnOrProjectArgument).Distinct();
List<WorkloadId> allWorkloadId = RunTargetToGetWorkloadIds(allProjects);
Reporter.WriteLine(string.Format(LocalizableStrings.InstallingWorkloads, string.Join(" ", allWorkloadId)));
var globalJsonPath = SdkDirectoryWorkloadManifestProvider.GetGlobalJsonPath(Environment.CurrentDirectory);
var workloadSetVersionFromGlobalJson = SdkDirectoryWorkloadManifestProvider.GlobalJsonReader.GetWorkloadVersionFromGlobalJson(globalJsonPath);

var workloadResolverFactory = new WorkloadResolverFactory();
var creationResult = workloadResolverFactory.Create();
var workloadInstaller = WorkloadInstallerFactory.GetWorkloadInstaller(NullReporter.Instance, new SdkFeatureBand(creationResult.SdkVersion),
creationResult.WorkloadResolver, Verbosity, creationResult.UserProfileDir, VerifySignatures, PackageDownloader,
creationResult.DotnetPath, TempDirectoryPath, null, RestoreActionConfiguration, elevationRequired: true);
var recorder = new WorkloadHistoryRecorder(
creationResult.WorkloadResolver,
workloadInstaller,
() => workloadResolverFactory.CreateForWorkloadSet(
creationResult.DotnetPath,
creationResult.SdkVersion.ToString(),
creationResult.UserProfileDir,
null));
recorder.HistoryRecord.CommandName = "restore";

recorder.Run(() =>
{
// If there's a workload set version specified in the global.json file, we need to make sure the workload set is installed before we try to discover workload ids.
if (!string.IsNullOrWhiteSpace(workloadSetVersionFromGlobalJson))
{
if (creationResult.WorkloadResolver.GetWorkloadManifestProvider() is SdkDirectoryWorkloadManifestProvider provider && provider.WouldThrowException())
Forgind marked this conversation as resolved.
Show resolved Hide resolved
{
new WorkloadUpdateCommand(_result).Execute();
}
}

var allProjects = DiscoverAllProjects(Directory.GetCurrentDirectory(), _slnOrProjectArgument).Distinct();
List<WorkloadId> allWorkloadId = RunTargetToGetWorkloadIds(allProjects);
Reporter.WriteLine(string.Format(LocalizableStrings.InstallingWorkloads, string.Join(" ", allWorkloadId)));

var workloadInstallCommand = new WorkloadInstallCommand(_result,
workloadIds: allWorkloadId.Select(a => a.ToString()).ToList().AsReadOnly());
workloadInstallCommand.IsRunningRestore = true;
var workloadInstallCommand = new WorkloadInstallCommand(_result,
workloadIds: allWorkloadId.Select(a => a.ToString()).ToList().AsReadOnly());
workloadInstallCommand.IsRunningRestore = true;

workloadInstallCommand.Execute();
workloadInstallCommand.Execute();
});

return 0;
}

Expand Down
Loading