Skip to content

Fix userlocal #41735

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

Merged
merged 4 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 6 additions & 4 deletions src/Cli/dotnet/commands/InstallingWorkloadCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ internal abstract class InstallingWorkloadCommand : WorkloadCommandBase
protected readonly string _downloadToCacheOption;
protected readonly string _dotnetPath;
protected readonly string _userProfileDir;
protected readonly string _workloadRootDir;
protected readonly bool _checkIfManifestExist;
protected readonly ReleaseVersion _sdkVersion;
protected readonly SdkFeatureBand _sdkFeatureBand;
Expand Down Expand Up @@ -92,8 +93,9 @@ public InstallingWorkloadCommand(

_dotnetPath = creationResult.DotnetPath;
_userProfileDir = creationResult.UserProfileDir;
_sdkVersion = creationResult.SdkVersion;
_sdkFeatureBand = new SdkFeatureBand(creationResult.SdkVersion);
_workloadRootDir = WorkloadFileBasedInstall.IsUserLocal(_dotnetPath, _sdkFeatureBand.ToString()) ? _userProfileDir : _dotnetPath;
Copy link
Member

Choose a reason for hiding this comment

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

Looking at the implementation of WorkloadFileBasedInstall.IsUserLocal, it has its own logic to calculate the feature band (in GetUserInstallFilePath). We should get rid of that and use SdkFeatureBand instead to keep everything consistent.

_sdkVersion = creationResult.SdkVersion;
_workloadResolver = creationResult.WorkloadResolver;
_targetSdkVersion ??= _sdkVersion;

Expand Down Expand Up @@ -125,7 +127,7 @@ protected static Dictionary<string, string> GetInstallStateContents(IEnumerable<

InstallStateContents GetCurrentInstallState()
{
return GetCurrentInstallState(_sdkFeatureBand, _dotnetPath);
return GetCurrentInstallState(_sdkFeatureBand, _workloadRootDir);
}

static InstallStateContents GetCurrentInstallState(SdkFeatureBand sdkFeatureBand, string dotnetDir)
Expand All @@ -141,7 +143,7 @@ public static bool ShouldUseWorkloadSetMode(SdkFeatureBand sdkFeatureBand, strin

protected void UpdateWorkloadManifests(ITransactionContext context, DirectoryPath? offlineCache)
{
var updateToLatestWorkloadSet = ShouldUseWorkloadSetMode(_sdkFeatureBand, _dotnetPath);
var updateToLatestWorkloadSet = ShouldUseWorkloadSetMode(_sdkFeatureBand, _workloadRootDir);
if (UseRollback && updateToLatestWorkloadSet)
{
// Rollback files are only for loose manifests. Update the mode to be loose manifests.
Expand All @@ -157,7 +159,7 @@ protected void UpdateWorkloadManifests(ITransactionContext context, DirectoryPat
// If a workload set version is specified, then switch to workload set update mode
// Check to make sure the value needs to be changed, as updating triggers a UAC prompt
// for MSI-based installs.
if (!ShouldUseWorkloadSetMode(_sdkFeatureBand, _dotnetPath))
if (!ShouldUseWorkloadSetMode(_sdkFeatureBand, _workloadRootDir))
{
_workloadInstaller.UpdateInstallMode(_sdkFeatureBand, true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ internal static void ShowWorkloadsInfo(ParseResult parseResult = null, WorkloadI
reporter.WriteLine($" Workload version: {workloadInfoHelper.ManifestProvider.GetWorkloadVersion()}");
}

var useWorkloadSets = InstallStateContents.FromPath(Path.Combine(WorkloadInstallType.GetInstallStateFolder(workloadInfoHelper._currentSdkFeatureBand, workloadInfoHelper.DotnetPath), "default.json")).UseWorkloadSets;
var useWorkloadSets = InstallStateContents.FromPath(Path.Combine(WorkloadInstallType.GetInstallStateFolder(workloadInfoHelper._currentSdkFeatureBand, workloadInfoHelper.UserLocalPath), "default.json")).UseWorkloadSets;
var workloadSetsString = useWorkloadSets == true ? "workload sets" : "loose manifests";
reporter.WriteLine(string.Format(CommonStrings.WorkloadManifestInstallationConfiguration, workloadSetsString));

Expand Down
3 changes: 3 additions & 0 deletions src/Cli/dotnet/commands/dotnet-workload/WorkloadInfoHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ internal class WorkloadInfoHelper : IWorkloadInfoHelper
public readonly SdkFeatureBand _currentSdkFeatureBand;
private readonly string _targetSdkVersion;
public string DotnetPath { get; }
public string UserLocalPath { get; }

public WorkloadInfoHelper(
bool isInteractive,
Expand Down Expand Up @@ -61,6 +62,8 @@ public WorkloadInfoHelper(
shouldLog: false);

WorkloadRecordRepo = workloadRecordRepo ?? Installer.GetWorkloadInstallationRecordRepository();

UserLocalPath = dotnetDir ?? (WorkloadFileBasedInstall.IsUserLocal(DotnetPath, _currentSdkFeatureBand.ToString()) ? userProfileDir : DotnetPath);
}

public IInstaller Installer { get; private init; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ internal class FileBasedInstaller : IInstaller
private const string InstalledWorkloadSetsDir = "InstalledWorkloadSets";
protected readonly string _dotnetDir;
protected readonly string _userProfileDir;
protected readonly string _workloadRootDir;
protected readonly DirectoryPath _tempPackagesDir;
private readonly INuGetPackageDownloader _nugetPackageDownloader;
private IWorkloadResolver _workloadResolver;
Expand Down Expand Up @@ -57,7 +58,8 @@ public FileBasedInstaller(IReporter reporter,
new FirstPartyNuGetPackageSigningVerifier(), logger,
restoreActionConfig: _restoreActionConfig);
bool userLocal = WorkloadFileBasedInstall.IsUserLocal(_dotnetDir, sdkFeatureBand.ToString());
_workloadMetadataDir = Path.Combine(userLocal ? _userProfileDir : _dotnetDir, "metadata", "workloads");
_workloadRootDir = userLocal ? _userProfileDir : _dotnetDir;
_workloadMetadataDir = Path.Combine(_workloadRootDir, "metadata", "workloads");
_reporter = reporter;
_sdkFeatureBand = sdkFeatureBand;
_workloadResolver = workloadResolver;
Expand Down Expand Up @@ -92,7 +94,7 @@ public WorkloadSet InstallWorkloadSet(ITransactionContext context, string worklo
string workloadSetPackageVersion = WorkloadSet.WorkloadSetVersionToWorkloadSetPackageVersion(workloadSetVersion, out workloadSetFeatureBand);
var workloadSetPackageId = GetManifestPackageId(new ManifestId("Microsoft.NET.Workloads"), workloadSetFeatureBand);

var workloadSetPath = Path.Combine(_dotnetDir, "sdk-manifests", _sdkFeatureBand.ToString(), "workloadsets", workloadSetVersion);
var workloadSetPath = Path.Combine(_workloadRootDir, "sdk-manifests", _sdkFeatureBand.ToString(), "workloadsets", workloadSetVersion);

try
{
Expand Down Expand Up @@ -226,9 +228,7 @@ public void RepairWorkloads(IEnumerable<WorkloadId> workloadIds, SdkFeatureBand

string GetManifestInstallDirForFeatureBand(string sdkFeatureBand)
{
string rootInstallDir = WorkloadFileBasedInstall.IsUserLocal(_dotnetDir, _sdkFeatureBand.ToString()) ? _userProfileDir : _dotnetDir;
var manifestInstallDir = Path.Combine(rootInstallDir, "sdk-manifests", sdkFeatureBand);
return manifestInstallDir;
return Path.Combine(_workloadRootDir, "sdk-manifests", sdkFeatureBand);
}

public void InstallWorkloadManifest(ManifestVersionUpdate manifestUpdate, ITransactionContext transactionContext, DirectoryPath? offlineCache = null)
Expand Down Expand Up @@ -341,7 +341,7 @@ public IEnumerable<WorkloadDownload> GetDownloads(IEnumerable<WorkloadId> worklo

public void GarbageCollect(Func<string, IWorkloadResolver> getResolverForWorkloadSet, DirectoryPath? offlineCache = null, bool cleanAllPacks = false)
{
var garbageCollector = new WorkloadGarbageCollector(_dotnetDir, _sdkFeatureBand, _installationRecordRepository.GetInstalledWorkloads(_sdkFeatureBand), getResolverForWorkloadSet, Reporter.Verbose);
var garbageCollector = new WorkloadGarbageCollector(_workloadRootDir, _sdkFeatureBand, _installationRecordRepository.GetInstalledWorkloads(_sdkFeatureBand), getResolverForWorkloadSet, Reporter.Verbose);
garbageCollector.Collect();

var featureBandsWithWorkloadInstallRecords = _installationRecordRepository.GetFeatureBandsWithInstallationRecords();
Expand Down Expand Up @@ -479,46 +479,36 @@ public void GarbageCollect(Func<string, IWorkloadResolver> getResolverForWorkloa

public void AdjustWorkloadSetInInstallState(SdkFeatureBand sdkFeatureBand, string workloadVersion)
{
string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetDir), "default.json");
Directory.CreateDirectory(Path.GetDirectoryName(path));
var installStateContents = InstallStateContents.FromPath(path);
installStateContents.WorkloadVersion = workloadVersion;
File.WriteAllText(path, installStateContents.ToString());
UpdateInstallState(sdkFeatureBand, contents => contents.WorkloadVersion = workloadVersion);
}

public void RemoveManifestsFromInstallState(SdkFeatureBand sdkFeatureBand)
{
string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetDir), "default.json");

if (File.Exists(path))
{
var installStateContents = InstallStateContents.FromString(File.ReadAllText(path));
installStateContents.Manifests = null;
File.WriteAllText(path, installStateContents.ToString());
}
UpdateInstallState(sdkFeatureBand, contents => contents.Manifests = null);
}

public void SaveInstallStateManifestVersions(SdkFeatureBand sdkFeatureBand, Dictionary<string, string> manifestContents)
{
string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetDir), "default.json");
Directory.CreateDirectory(Path.GetDirectoryName(path));
var installStateContents = InstallStateContents.FromPath(path);
installStateContents.Manifests = manifestContents;
File.WriteAllText(path, installStateContents.ToString());
UpdateInstallState(sdkFeatureBand, contents => contents.Manifests = manifestContents);
}

public void UpdateInstallMode(SdkFeatureBand sdkFeatureBand, bool? newMode)
{
string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(sdkFeatureBand, _dotnetDir), "default.json");
Directory.CreateDirectory(Path.GetDirectoryName(path));
var installStateContents = InstallStateContents.FromPath(path);
installStateContents.UseWorkloadSets = newMode;
File.WriteAllText(path, installStateContents.ToString());
UpdateInstallState(sdkFeatureBand, contents => contents.UseWorkloadSets = newMode);

var newModeString = newMode == null ? "<null>" : (newMode.Value ? WorkloadConfigCommandParser.UpdateMode_WorkloadSet : WorkloadConfigCommandParser.UpdateMode_Manifests);
_reporter.WriteLine(string.Format(LocalizableStrings.UpdatedWorkloadMode, newModeString));
}

private void UpdateInstallState(SdkFeatureBand sdkFeatureBand, Action<InstallStateContents> update)
Copy link
Member

Choose a reason for hiding this comment

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

Nice refactoring. 👍

{
string path = Path.Combine(WorkloadInstallType.GetInstallStateFolder(sdkFeatureBand, _workloadRootDir), "default.json");
Directory.CreateDirectory(Path.GetDirectoryName(path));
var installStateContents = InstallStateContents.FromPath(path);
update(installStateContents);
File.WriteAllText(path, installStateContents.ToString());
}

/// <summary>
/// Remove all workload installation records that aren't from Visual Studio.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ public override int Execute()

if (!_skipManifestUpdate)
{
var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _dotnetPath), "default.json");
var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkFeatureBand, _workloadRootDir), "default.json");
if (string.IsNullOrWhiteSpace(_fromRollbackDefinition) &&
!SpecifiedWorkloadSetVersionOnCommandLine &&
!SpecifiedWorkloadSetVersionInGlobalJson &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public override int Execute()
Reporter.WriteLine();
var shouldPrintTable = globalJsonInformation?.WorkloadVersionInstalled != false;
var shouldShowWorkloadSetVersion = globalJsonInformation is not null ||
InstallStateContents.FromPath(Path.Combine(WorkloadInstallType.GetInstallStateFolder(_workloadListHelper._currentSdkFeatureBand, _workloadListHelper.DotnetPath), "default.json")).UseWorkloadSets == true;
InstallStateContents.FromPath(Path.Combine(WorkloadInstallType.GetInstallStateFolder(_workloadListHelper._currentSdkFeatureBand, _workloadListHelper.UserLocalPath), "default.json")).UseWorkloadSets == true;

if (shouldShowWorkloadSetVersion)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public override int Execute()
{
_workloadManifestUpdater.UpdateAdvertisingManifestsAsync(
_includePreviews,
ShouldUseWorkloadSetMode(_sdkFeatureBand, _dotnetPath),
ShouldUseWorkloadSetMode(_sdkFeatureBand, _workloadRootDir),
string.IsNullOrWhiteSpace(_fromCacheOption) ?
null :
new DirectoryPath(_fromCacheOption))
Expand Down
3 changes: 2 additions & 1 deletion src/Common/WorkloadFileBasedInstall.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Microsoft.NET.Sdk.WorkloadManifestReader;

namespace Microsoft.DotNet.Workloads.Workload
{
Expand Down Expand Up @@ -34,7 +35,7 @@ static int Last2DigitsTo0(int versionBuild)
sdkFeatureBand = $"{sdkVersionParsed.Major}.{sdkVersionParsed.Minor}.{Last2DigitsTo0(sdkVersionParsed.Build)}";
}

return Path.Combine(dotnetDir, "metadata", "workloads", sdkFeatureBand, "userlocal");
return Path.Combine(dotnetDir, "metadata", "workloads", new SdkFeatureBand(sdkFeatureBand).ToString(), "userlocal");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public partial class SdkDirectoryWorkloadManifestProvider : IWorkloadManifestPro
public const string WorkloadSetsFolderName = "workloadsets";

private readonly string _sdkRootPath;
private readonly string _sdkOrUserLocalPath;
private readonly SdkFeatureBand _sdkVersionBand;
private readonly string[] _manifestRoots;
private static HashSet<string> _outdatedManifestIds = new(StringComparer.OrdinalIgnoreCase) { "microsoft.net.workload.android", "microsoft.net.workload.blazorwebassembly", "microsoft.net.workload.ios",
Expand Down Expand Up @@ -67,6 +68,23 @@ internal SdkDirectoryWorkloadManifestProvider(string sdkRootPath, string sdkVers
_workloadSetVersionFromConstructor = workloadSetVersion;
_globalJsonPathFromConstructor = globalJsonPath;

string? userManifestsRoot = userProfileDir is null ? null : Path.Combine(userProfileDir, "sdk-manifests");
string dotnetManifestRoot = Path.Combine(_sdkRootPath, "sdk-manifests");
if (userManifestsRoot != null && WorkloadFileBasedInstall.IsUserLocal(_sdkRootPath, _sdkVersionBand.ToString()) && Directory.Exists(userManifestsRoot))
{
_sdkOrUserLocalPath = userProfileDir ?? _sdkRootPath;
if (getEnvironmentVariable(EnvironmentVariableNames.WORKLOAD_MANIFEST_IGNORE_DEFAULT_ROOTS) == null)
{
_manifestRoots = new[] { userManifestsRoot, dotnetManifestRoot };
}
}
else if (getEnvironmentVariable(EnvironmentVariableNames.WORKLOAD_MANIFEST_IGNORE_DEFAULT_ROOTS) == null)
{
_manifestRoots = new[] { dotnetManifestRoot };
}

_sdkOrUserLocalPath ??= _sdkRootPath;

var knownManifestIdsFilePath = Path.Combine(_sdkRootPath, "sdk", sdkVersion, "KnownWorkloadManifests.txt");
if (!File.Exists(knownManifestIdsFilePath))
{
Expand All @@ -83,20 +101,6 @@ internal SdkDirectoryWorkloadManifestProvider(string sdkRootPath, string sdkVers
}
}

if (getEnvironmentVariable(EnvironmentVariableNames.WORKLOAD_MANIFEST_IGNORE_DEFAULT_ROOTS) == null)
{
string? userManifestsRoot = userProfileDir is null ? null : Path.Combine(userProfileDir, "sdk-manifests");
string dotnetManifestRoot = Path.Combine(_sdkRootPath, "sdk-manifests");
if (userManifestsRoot != null && WorkloadFileBasedInstall.IsUserLocal(_sdkRootPath, _sdkVersionBand.ToString()) && Directory.Exists(userManifestsRoot))
{
_manifestRoots = new[] { userManifestsRoot, dotnetManifestRoot };
}
else
{
_manifestRoots = new[] { dotnetManifestRoot };
}
}

var manifestDirectoryEnvironmentVariable = getEnvironmentVariable(EnvironmentVariableNames.WORKLOAD_MANIFEST_ROOTS);
if (manifestDirectoryEnvironmentVariable != null)
{
Expand Down Expand Up @@ -179,7 +183,7 @@ bool TryGetWorkloadSet(string workloadSetVersion, out WorkloadSet? workloadSet)

if (_workloadSet is null)
{
var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkVersionBand, _sdkRootPath), "default.json");
var installStateFilePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(_sdkVersionBand, _sdkOrUserLocalPath), "default.json");
if (File.Exists(installStateFilePath))
{
var installState = InstallStateContents.FromPath(installStateFilePath);
Expand Down
10 changes: 10 additions & 0 deletions test/dotnet-workload-update.Tests/GivenDotnetWorkloadUpdate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,17 @@ public void GivenWorkloadUpdateAcrossFeatureBandsItUpdatesPacks(bool userLocal)
var updateParseResult = Parser.Instance.Parse(new string[] { "dotnet", "workload", "update", "--from-previous-sdk" });
var updateCommand = new WorkloadUpdateCommand(updateParseResult, reporter: _reporter, workloadResolverFactory, nugetPackageDownloader: nugetDownloader,
workloadManifestUpdater: manifestUpdater, tempDirPath: testDirectory);
var installStatePath = Path.Combine(WorkloadInstallType.GetInstallStateFolder(new SdkFeatureBand(sdkFeatureVersion), installRoot), "default.json");
var oldInstallState = InstallStateContents.FromPath(installStatePath);
oldInstallState.Manifests = new Dictionary<string, string>()
{
{installingWorkload, $"6.0.102/{sdkFeatureVersion}" }
};
Directory.CreateDirectory(Path.GetDirectoryName(installStatePath));
File.WriteAllText(installStatePath, oldInstallState.ToString());
updateCommand.Execute();
var newInstallState = InstallStateContents.FromPath(installStatePath);
newInstallState.Manifests.Should().BeNull();

foreach (var pack in workloadPacks)
{
Expand Down
Loading