diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 2c658e58f0b4..5e8189ad7e95 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -105,9 +105,9 @@ https://github.com/dotnet/aspnetcore d50065c4a4fe31a66a1cc2e1a31896d30464da13 - + https://github.com/nuget/nuget.client - 1a082949ae5b6da7ca2cce047396c53ae1afdde7 + 125f673fd1cdb3cc012f62aa3ce764d2460b89eb https://github.com/microsoft/vstest @@ -246,13 +246,13 @@ 9a1c3e1b7f0c8763d4c96e593961a61a72679a7b - + https://github.com/dotnet/roslyn-analyzers - 846a766f73caa82608db6fee9f2860004298449f + ea9fb45000311153bfc91690f306cca2b80e6b83 - + https://github.com/dotnet/roslyn-analyzers - 846a766f73caa82608db6fee9f2860004298449f + ea9fb45000311153bfc91690f306cca2b80e6b83 diff --git a/eng/Versions.props b/eng/Versions.props index 1460f902048c..d770b5f19ff3 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -16,7 +16,7 @@ - false + true release @@ -63,7 +63,7 @@ - 6.4.0-preview.3.107 + 6.4.0-rc.117 $(NuGetBuildTasksPackageVersion) 6.0.0-rc.278 $(NuGetBuildTasksPackageVersion) @@ -102,7 +102,7 @@ - 7.0.0-preview1.22504.1 + 7.0.0-preview1.22513.1 @@ -112,7 +112,7 @@ so target one that matches the version in minimumMSBuildVersion. This avoids the need to juggle references to packages that have been updated in newer MSBuild. --> - $([System.IO.File]::ReadAllText('$(RepoRoot)\src\Layout\redist\minimumMSBuildVersion').Trim()) + $([System.IO.File]::ReadAllText('$(RepoRoot)\src\Layout\redist\minimumMSBuildVersion').Trim()) $(MicrosoftBuildPackageVersion) $(MicrosoftBuildPackageVersion) 17.5.0-preview-22513-03 diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/DiagnosticIds.cs b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/DiagnosticIds.cs index cd86e7d20eb9..ef25359022e4 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/DiagnosticIds.cs +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/DiagnosticIds.cs @@ -26,6 +26,8 @@ public static class DiagnosticIds public const string CannotAddAttribute = "CP0016"; public const string CannotChangeParameterName = "CP0017"; public const string CannotAddSealedToInterfaceMember = "CP0018"; + public const string CannotReduceVisibility = "CP0019"; + public const string CannotExpandVisibility = "CP0020"; // Assembly loading ids public const string AssemblyNotFound = "CP1001"; diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Resources.resx b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Resources.resx index 7473dcfa8549..9d810ecb933a 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Resources.resx +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Resources.resx @@ -240,4 +240,10 @@ Cannot add sealed keyword to default interface member '{0}'. + + Visibility of '{0}' expanded from '{1}' to '{2}'. + + + Visibility of '{0}' reduced from '{1}' to '{2}'. + \ No newline at end of file diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/AttributesMustMatch.cs b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/AttributesMustMatch.cs index bd078b4874d4..d4e8ff594acd 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/AttributesMustMatch.cs +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/AttributesMustMatch.cs @@ -62,6 +62,11 @@ private void AddDifference(IList differences, DifferenceType d return; } + if (!_settings.StrictMode && dt == DifferenceType.Added) + { + return; + } + CompatDifference difference = dt switch { DifferenceType.Changed => new CompatDifference( @@ -167,10 +172,17 @@ private void ReportAttributeDifferences(ISymbol containing, for (int i = 0; i < rightGroup.Attributes.Count; i++) { - if (!rightGroup.Seen[i]) + if (!rightGroup.Seen[i] && _settings.StrictMode) { // Attribute arguments exist on right but not left. - // Issue "changed" diagnostic. + // Left + // [Foo("a")] + // void F() + // Right + // [Foo("a")] + // [Foo("b")] + // void F() + // Issue "changed" diagnostic when in strict mode. AddDifference(differences, DifferenceType.Changed, leftMetadata, rightMetadata, containing, itemRef, rightGroup.Attributes[i]); } } diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotAddOrRemoveVirtualKeyword.cs b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotAddOrRemoveVirtualKeyword.cs index a2be104545fe..1bb7d5ad2198 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotAddOrRemoveVirtualKeyword.cs +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotAddOrRemoveVirtualKeyword.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis; using Microsoft.DotNet.ApiCompatibility.Abstractions; +using Microsoft.DotNet.ApiCompatibility.Extensions; namespace Microsoft.DotNet.ApiCompatibility.Rules { @@ -50,6 +51,12 @@ private void RunOnMemberSymbol(ISymbol? left, ISymbol? right, ITypeSymbol leftCo if (left.IsVirtual) { + // Removing the virtual keyword from a member in a sealed type won't be a breaking change. + if (leftContainingType.IsEffectivelySealed(_settings.IncludeInternalSymbols)) + { + return; + } + // If left is virtual and right is not, then emit a diagnostic // specifying that the virtual modifier cannot be removed. if (!right.IsVirtual) diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotChangeVisibility.cs b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotChangeVisibility.cs new file mode 100644 index 000000000000..5a5ffcd950e2 --- /dev/null +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/CannotChangeVisibility.cs @@ -0,0 +1,115 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; +using System.Runtime; +using Microsoft.CodeAnalysis; +using Microsoft.DotNet.ApiCompatibility.Abstractions; +using Microsoft.DotNet.ApiCompatibility.Extensions; + +namespace Microsoft.DotNet.ApiCompatibility.Rules +{ + /// + /// This class implements a rule to check that the visibility of symbols is not reduced. + /// In strict mode, it also checks that the visibility isn't expanded. + /// + public class CannotChangeVisibility : IRule + { + private readonly RuleSettings _settings; + + public CannotChangeVisibility(RuleSettings settings, IRuleRegistrationContext context) + { + _settings = settings; + context.RegisterOnMemberSymbolAction(RunOnMemberSymbol); + context.RegisterOnTypeSymbolAction(RunOnTypeSymbol); + } + + private static Accessibility NormalizeInternals(Accessibility a) => a switch + { + Accessibility.ProtectedOrInternal => Accessibility.Protected, + Accessibility.ProtectedAndInternal or Accessibility.Internal => Accessibility.Private, + _ => a, + }; + + private int CompareAccessibility(Accessibility a, Accessibility b) + { + if (!_settings.IncludeInternalSymbols) + { + a = NormalizeInternals(a); + b = NormalizeInternals(b); + } + + if (a == b) + { + return 0; + } + + return (a, b) switch + { + (Accessibility.Public, _) => 1, + (_, Accessibility.Public) => -1, + (Accessibility.ProtectedOrInternal, _) => 1, + (_, Accessibility.ProtectedOrInternal) => -1, + (Accessibility.Protected or Accessibility.Internal, _) => 1, + (_, Accessibility.Protected or Accessibility.Internal) => -1, + (Accessibility.ProtectedAndInternal, _) => 1, + (_, Accessibility.ProtectedAndInternal) => -1, + _ => throw new NotImplementedException(), + }; + } + + private void RunOnSymbol( + ISymbol? left, + ISymbol? right, + MetadataInformation leftMetadata, + MetadataInformation rightMetadata, + IList differences) + { + // The MemberMustExist rule handles missing symbols and therefore this rule only runs when left and right is not null. + if (left is null || right is null) + { + return; + } + + Accessibility leftAccess = left.DeclaredAccessibility; + Accessibility rightAccess = right.DeclaredAccessibility; + int accessComparison = CompareAccessibility(leftAccess, rightAccess); + + if (accessComparison > 0) + { + differences.Add(new CompatDifference(leftMetadata, + rightMetadata, + DiagnosticIds.CannotReduceVisibility, + string.Format(Resources.CannotReduceVisibility, left, leftAccess, rightAccess), + DifferenceType.Changed, + left)); + } + else if (_settings.StrictMode && accessComparison < 0) + { + differences.Add(new CompatDifference(leftMetadata, + rightMetadata, + DiagnosticIds.CannotExpandVisibility, + string.Format(Resources.CannotExpandVisibility, right, leftAccess, rightAccess), + DifferenceType.Changed, + right)); + } + } + + private void RunOnTypeSymbol( + ITypeSymbol? left, + ITypeSymbol? right, + MetadataInformation leftMetadata, + MetadataInformation rightMetadata, + IList differences) => RunOnSymbol(left, right, leftMetadata, rightMetadata, differences); + + private void RunOnMemberSymbol( + ISymbol? left, + ISymbol? right, + ITypeSymbol leftContainingType, + ITypeSymbol rightContainingType, + MetadataInformation leftMetadata, + MetadataInformation rightMetadata, + IList differences) => RunOnSymbol(left, right, leftMetadata, rightMetadata, differences); + } +} diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/RuleFactory.cs b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/RuleFactory.cs index 341b875bd16e..747a644dca39 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/RuleFactory.cs +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/Rules/RuleFactory.cs @@ -39,7 +39,8 @@ public IRule[] CreateRules(RuleSettings settings, IRuleRegistrationContext conte new CannotRemoveBaseTypeOrInterface(settings, context), new CannotSealType(settings, context), new EnumsMustMatch(settings, context), - new MembersMustExist(settings, context) + new MembersMustExist(settings, context), + new CannotChangeVisibility(settings, context) }; if (_enableRuleAttributesMustMatch) diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.cs.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.cs.xlf index 27cc9270a65c..131100e6ca39 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.cs.xlf +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.cs.xlf @@ -87,6 +87,16 @@ Název parametru u člena {0} se změnil z {1} na {2}. + + Visibility of '{0}' expanded from '{1}' to '{2}'. + Visibility of '{0}' expanded from '{1}' to '{2}'. + + + + Visibility of '{0}' reduced from '{1}' to '{2}'. + Visibility of '{0}' reduced from '{1}' to '{2}'. + + Cannot remove attribute '{0}' from '{1}'. Atribut {0} nelze odebrat z {1}. diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.de.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.de.xlf index 148f10710e5b..d29904e874cf 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.de.xlf +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.de.xlf @@ -87,6 +87,16 @@ Der Parametername des Members "{0}" wurde von "{1}" in "{2}" geändert. + + Visibility of '{0}' expanded from '{1}' to '{2}'. + Visibility of '{0}' expanded from '{1}' to '{2}'. + + + + Visibility of '{0}' reduced from '{1}' to '{2}'. + Visibility of '{0}' reduced from '{1}' to '{2}'. + + Cannot remove attribute '{0}' from '{1}'. Das Attribut "{0}" kann nicht aus "{1}" entfernt werden. diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.es.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.es.xlf index fc456bf2c297..6721143f8649 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.es.xlf +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.es.xlf @@ -87,6 +87,16 @@ El nombre de parámetro del miembro '{0}' cambió de '{1}' a '{2}'. + + Visibility of '{0}' expanded from '{1}' to '{2}'. + Visibility of '{0}' expanded from '{1}' to '{2}'. + + + + Visibility of '{0}' reduced from '{1}' to '{2}'. + Visibility of '{0}' reduced from '{1}' to '{2}'. + + Cannot remove attribute '{0}' from '{1}'. No se puede quitar el atributo '{0}' de '{1}'. diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.fr.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.fr.xlf index 6655b69e4083..2ad359cfdc97 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.fr.xlf +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.fr.xlf @@ -87,6 +87,16 @@ Le nom du paramètre sur le membre « {0} » est passé de « {1} » à « {2} ». + + Visibility of '{0}' expanded from '{1}' to '{2}'. + Visibility of '{0}' expanded from '{1}' to '{2}'. + + + + Visibility of '{0}' reduced from '{1}' to '{2}'. + Visibility of '{0}' reduced from '{1}' to '{2}'. + + Cannot remove attribute '{0}' from '{1}'. Impossible de supprimer l’attribut « {0} » de « {1} ». diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.it.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.it.xlf index c36c61b78cff..289caf6c8a81 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.it.xlf +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.it.xlf @@ -87,6 +87,16 @@ Il nome del parametro nel membro '{0}' è stato modificato da '{1}' a '{2}'. + + Visibility of '{0}' expanded from '{1}' to '{2}'. + Visibility of '{0}' expanded from '{1}' to '{2}'. + + + + Visibility of '{0}' reduced from '{1}' to '{2}'. + Visibility of '{0}' reduced from '{1}' to '{2}'. + + Cannot remove attribute '{0}' from '{1}'. Non è possibile rimuovere l'attributo '{0}' da '{1}'. diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ja.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ja.xlf index f9df9120294d..6acabcccea94 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ja.xlf +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ja.xlf @@ -87,6 +87,16 @@ メンバー '{0}' のパラメーター名が '{1}' から '{2}' に変更されました。 + + Visibility of '{0}' expanded from '{1}' to '{2}'. + Visibility of '{0}' expanded from '{1}' to '{2}'. + + + + Visibility of '{0}' reduced from '{1}' to '{2}'. + Visibility of '{0}' reduced from '{1}' to '{2}'. + + Cannot remove attribute '{0}' from '{1}'. 属性 '{0}' を '{1}' から削除できません。 diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ko.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ko.xlf index 2b3f366bad4e..60517def4b2b 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ko.xlf +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ko.xlf @@ -87,6 +87,16 @@ 멤버 '{0}'의 매개 변수 이름이 '{1}'에서 '{2}'(으)로 변경되었습니다. + + Visibility of '{0}' expanded from '{1}' to '{2}'. + Visibility of '{0}' expanded from '{1}' to '{2}'. + + + + Visibility of '{0}' reduced from '{1}' to '{2}'. + Visibility of '{0}' reduced from '{1}' to '{2}'. + + Cannot remove attribute '{0}' from '{1}'. '{1}'에서 특성 '{0}'을(를) 제거할 수 없습니다. diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pl.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pl.xlf index e30706482074..490b7be942b1 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pl.xlf +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pl.xlf @@ -87,6 +87,16 @@ Nazwa parametru elementu członkowskiego „{0}” została zmieniona z „{1}” na „{2}”. + + Visibility of '{0}' expanded from '{1}' to '{2}'. + Visibility of '{0}' expanded from '{1}' to '{2}'. + + + + Visibility of '{0}' reduced from '{1}' to '{2}'. + Visibility of '{0}' reduced from '{1}' to '{2}'. + + Cannot remove attribute '{0}' from '{1}'. Nie można usunąć atrybutu „{0}” z elementu „{1}”. diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pt-BR.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pt-BR.xlf index a7bac27d9eb1..13b6c11d7070 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pt-BR.xlf +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.pt-BR.xlf @@ -87,6 +87,16 @@ Nome do parâmetro no membro '{0}' alterado de '{1}' para '{2}'. + + Visibility of '{0}' expanded from '{1}' to '{2}'. + Visibility of '{0}' expanded from '{1}' to '{2}'. + + + + Visibility of '{0}' reduced from '{1}' to '{2}'. + Visibility of '{0}' reduced from '{1}' to '{2}'. + + Cannot remove attribute '{0}' from '{1}'. Não é possível remover o atributo '{0}' de '{1}'. diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ru.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ru.xlf index 128fe9f46c58..cc5d247c856f 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ru.xlf +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.ru.xlf @@ -87,6 +87,16 @@ Имя параметра на члене "{0}" изменено с "{1}" на "{2}". + + Visibility of '{0}' expanded from '{1}' to '{2}'. + Visibility of '{0}' expanded from '{1}' to '{2}'. + + + + Visibility of '{0}' reduced from '{1}' to '{2}'. + Visibility of '{0}' reduced from '{1}' to '{2}'. + + Cannot remove attribute '{0}' from '{1}'. Не удается удалить атрибут "{0}" из "{1}". diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.tr.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.tr.xlf index da71a633b1ea..9e2b91f92e5d 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.tr.xlf +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.tr.xlf @@ -87,6 +87,16 @@ '{0}' üyesinin parametre adı '{1}' iken '{2}' olarak değiştirildi. + + Visibility of '{0}' expanded from '{1}' to '{2}'. + Visibility of '{0}' expanded from '{1}' to '{2}'. + + + + Visibility of '{0}' reduced from '{1}' to '{2}'. + Visibility of '{0}' reduced from '{1}' to '{2}'. + + Cannot remove attribute '{0}' from '{1}'. '{0}' özniteliği '{1}' sınıfından kaldırılamıyor. diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hans.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hans.xlf index d8ccd28402ec..1ff387a8d4aa 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hans.xlf +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hans.xlf @@ -87,6 +87,16 @@ 成员 "{0}" 上的参数名称已从 "{1}" 更改为 "{2}"。 + + Visibility of '{0}' expanded from '{1}' to '{2}'. + Visibility of '{0}' expanded from '{1}' to '{2}'. + + + + Visibility of '{0}' reduced from '{1}' to '{2}'. + Visibility of '{0}' reduced from '{1}' to '{2}'. + + Cannot remove attribute '{0}' from '{1}'. 无法从 "{1}" 中删除属性 "{0}"。 diff --git a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hant.xlf b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hant.xlf index c6e3479330c8..caa004f4c38f 100644 --- a/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hant.xlf +++ b/src/ApiCompat/Microsoft.DotNet.ApiCompatibility/xlf/Resources.zh-Hant.xlf @@ -87,6 +87,16 @@ 成員 '{0}' 上的參數名稱從 '{1}' 變更為 '{2}'。 + + Visibility of '{0}' expanded from '{1}' to '{2}'. + Visibility of '{0}' expanded from '{1}' to '{2}'. + + + + Visibility of '{0}' reduced from '{1}' to '{2}'. + Visibility of '{0}' reduced from '{1}' to '{2}'. + + Cannot remove attribute '{0}' from '{1}'. 無法從 '{1}' 移除屬性 '{0}'。 diff --git a/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs b/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs index 77e0a621cdce..454534779a5d 100644 --- a/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs +++ b/src/BuiltInTools/dotnet-watch/Internal/MsBuildFileSetFactory.cs @@ -63,7 +63,7 @@ internal MsBuildFileSetFactory( public async Task CreateAsync(CancellationToken cancellationToken) { - var watchList = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + var watchList = Path.GetTempFileName(); try { var projectDir = Path.GetDirectoryName(_projectFile); diff --git a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj index 636f43d119b4..065f750f7200 100644 --- a/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj +++ b/src/BuiltInTools/dotnet-watch/dotnet-watch.csproj @@ -23,6 +23,7 @@ + diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj b/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj index 03ea923734ef..b954e2c4659d 100644 --- a/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj +++ b/src/Cli/Microsoft.DotNet.Cli.Utils/Microsoft.DotNet.Cli.Utils.csproj @@ -21,8 +21,12 @@ + $(PkgMicrosoft_Build_Runtime)\contentFiles\any\net6.0\MSBuild.dll + $(PkgMicrosoft_Build_Runtime)\contentFiles\any\net7.0\MSBuild.dll diff --git a/src/Cli/Microsoft.DotNet.InternalAbstractions/DirectoryWrapper.cs b/src/Cli/Microsoft.DotNet.InternalAbstractions/DirectoryWrapper.cs index 312d6fecd7d4..b063b9fc4a08 100644 --- a/src/Cli/Microsoft.DotNet.InternalAbstractions/DirectoryWrapper.cs +++ b/src/Cli/Microsoft.DotNet.InternalAbstractions/DirectoryWrapper.cs @@ -7,7 +7,7 @@ namespace Microsoft.Extensions.EnvironmentAbstractions { - internal class DirectoryWrapper: IDirectory + internal class DirectoryWrapper : IDirectory { public bool Exists(string path) { @@ -19,6 +19,11 @@ public ITemporaryDirectory CreateTemporaryDirectory() return new TemporaryDirectory(); } + public string CreateTemporarySubdirectory() + { + return CreateTemporaryDirectory().DirectoryPath; + } + public IEnumerable EnumerateDirectories(string path) { return Directory.EnumerateDirectories(path); diff --git a/src/Cli/Microsoft.DotNet.InternalAbstractions/FilePath.cs b/src/Cli/Microsoft.DotNet.InternalAbstractions/FilePath.cs index 06ed28ab76d7..cf69976a0844 100644 --- a/src/Cli/Microsoft.DotNet.InternalAbstractions/FilePath.cs +++ b/src/Cli/Microsoft.DotNet.InternalAbstractions/FilePath.cs @@ -30,7 +30,7 @@ public string ToQuotedString() public override string ToString() { - return ToQuotedString(); + return Value; } public DirectoryPath GetDirectoryPath() diff --git a/src/Cli/Microsoft.DotNet.InternalAbstractions/IDirectory.cs b/src/Cli/Microsoft.DotNet.InternalAbstractions/IDirectory.cs index 983bba34afc3..0a2e4e31e09f 100644 --- a/src/Cli/Microsoft.DotNet.InternalAbstractions/IDirectory.cs +++ b/src/Cli/Microsoft.DotNet.InternalAbstractions/IDirectory.cs @@ -24,5 +24,9 @@ internal interface IDirectory void Delete(string path, bool recursive); void Move(string source, string destination); + + + /// Returns a new directory created under the temp folder. Can be on the mock under test or the real temp file folder. + string CreateTemporarySubdirectory(); } } diff --git a/src/Cli/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.csproj b/src/Cli/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.csproj index 041ba1315c78..6260c984866d 100644 --- a/src/Cli/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.csproj +++ b/src/Cli/Microsoft.DotNet.InternalAbstractions/Microsoft.DotNet.InternalAbstractions.csproj @@ -12,4 +12,7 @@ true + + + diff --git a/src/Cli/Microsoft.DotNet.InternalAbstractions/TemporaryDirectory.cs b/src/Cli/Microsoft.DotNet.InternalAbstractions/TemporaryDirectory.cs index d43683e156f5..9ce8241c8967 100644 --- a/src/Cli/Microsoft.DotNet.InternalAbstractions/TemporaryDirectory.cs +++ b/src/Cli/Microsoft.DotNet.InternalAbstractions/TemporaryDirectory.cs @@ -1,8 +1,9 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using Microsoft.Extensions.EnvironmentAbstractions; using System.IO; +using Microsoft.Extensions.EnvironmentAbstractions; +using Microsoft.DotNet; namespace Microsoft.DotNet.InternalAbstractions { @@ -12,8 +13,7 @@ internal class TemporaryDirectory : ITemporaryDirectory public TemporaryDirectory() { - DirectoryPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); - Directory.CreateDirectory(DirectoryPath); + DirectoryPath = Path.Combine(PathUtilities.CreateTempSubdirectory()); } public void Dispose() diff --git a/src/Cli/dotnet/ShellShim/ShellShimRepository.cs b/src/Cli/dotnet/ShellShim/ShellShimRepository.cs index 4dee9748d079..68390f1f2ce2 100644 --- a/src/Cli/dotnet/ShellShim/ShellShimRepository.cs +++ b/src/Cli/dotnet/ShellShim/ShellShimRepository.cs @@ -85,7 +85,8 @@ public void CreateShim(FilePath targetExecutablePath, ToolCommandName commandNam ex); } }, - rollback: () => { + rollback: () => + { foreach (var file in GetShimFiles(commandName).Where(f => _fileSystem.File.Exists(f.Value))) { File.Delete(file.Value); @@ -97,12 +98,13 @@ public void RemoveShim(ToolCommandName commandName) { var files = new Dictionary(); TransactionalAction.Run( - action: () => { + action: () => + { try { foreach (var file in GetShimFiles(commandName).Where(f => _fileSystem.File.Exists(f.Value))) { - var tempPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + var tempPath = Path.Combine(_fileSystem.Directory.CreateTemporarySubdirectory(), Path.GetRandomFileName()); FileAccessRetrier.RetryOnMoveAccessFailure(() => _fileSystem.File.Move(file.Value, tempPath)); files[file.Value] = tempPath; } @@ -118,13 +120,15 @@ public void RemoveShim(ToolCommandName commandName) ex); } }, - commit: () => { + commit: () => + { foreach (var value in files.Values) { _fileSystem.File.Delete(value); } }, - rollback: () => { + rollback: () => + { foreach (var kvp in files) { FileAccessRetrier.RetryOnMoveAccessFailure(() => _fileSystem.File.Move(kvp.Value, kvp.Key)); diff --git a/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs b/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs index 799c99fbec39..8b04671a631b 100644 --- a/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs +++ b/src/Cli/dotnet/SudoEnvironmentDirectoryOverride.cs @@ -3,11 +3,9 @@ using System; using System.CommandLine; -using System.CommandLine.Parsing; using System.IO; using System.Linq; using Microsoft.DotNet.Cli.Utils; -using Microsoft.DotNet.Tools.Common; using NuGet.Common; using NuGet.Configuration; @@ -18,8 +16,6 @@ namespace Microsoft.DotNet.Cli /// public static class SudoEnvironmentDirectoryOverride { - private const string SudoHomeDirectory = "/tmp/dotnet_sudo_home/"; - /// /// Not for security use. Detect if command is running under sudo /// via if SUDO_UID being set. @@ -38,22 +34,9 @@ public static void OverrideEnvironmentVariableToTmp(ParseResult parseResult) { if (!OperatingSystem.IsWindows() && IsRunningUnderSudo() && IsRunningWorkloadCommand(parseResult)) { - if (!TempHomeIsOnlyRootWritable(SudoHomeDirectory)) - { - try - { - Directory.Delete(SudoHomeDirectory, recursive: true); - } - catch (DirectoryNotFoundException) - { - // Avoid read after write race condition - } - } - - Directory.CreateDirectory(SudoHomeDirectory); - + string sudoHome = PathUtilities.CreateTempSubdirectory(); var homeBeforeOverride = Path.Combine(Environment.GetEnvironmentVariable("HOME")); - Environment.SetEnvironmentVariable("HOME", SudoHomeDirectory); + Environment.SetEnvironmentVariable("HOME", sudoHome); CopyUserNuGetConfigToOverriddenHome(homeBeforeOverride); } @@ -107,31 +90,5 @@ private static void CopyUserNuGetConfigToOverriddenHome(string homeBeforeOverrid private static bool IsRunningWorkloadCommand(ParseResult parseResult) => parseResult.RootSubCommandResult() == (WorkloadCommandParser.GetCommand().Name); - - private static bool TempHomeIsOnlyRootWritable(string path) - { - if (StatInterop.LStat(path, out StatInterop.FileStatus fileStat) != 0) - { - return false; - } - - return IsOwnedByRoot(fileStat) && GroupCannotWrite(fileStat) && - OtherUserCannotWrite(fileStat); - } - - private static bool OtherUserCannotWrite(StatInterop.FileStatus fileStat) - { - return (fileStat.Mode & (int) StatInterop.Permissions.S_IWOTH) == 0; - } - - private static bool GroupCannotWrite(StatInterop.FileStatus fileStat) - { - return (fileStat.Mode & (int) StatInterop.Permissions.S_IWGRP) == 0; - } - - private static bool IsOwnedByRoot(StatInterop.FileStatus fileStat) - { - return fileStat.Uid == 0; - } } } diff --git a/src/Cli/dotnet/ToolPackage/ToolPackageInstaller.cs b/src/Cli/dotnet/ToolPackage/ToolPackageInstaller.cs index 1f3a37d9ee0f..f9d20bf96648 100644 --- a/src/Cli/dotnet/ToolPackage/ToolPackageInstaller.cs +++ b/src/Cli/dotnet/ToolPackage/ToolPackageInstaller.cs @@ -11,7 +11,6 @@ using Microsoft.DotNet.Configurer; using Microsoft.DotNet.Tools; using Microsoft.Extensions.EnvironmentAbstractions; -using NuGet.ProjectModel; using NuGet.Versioning; namespace Microsoft.DotNet.ToolPackage @@ -46,17 +45,18 @@ public IToolPackage InstallPackage( string rollbackDirectory = null; return TransactionalAction.Run( - action: () => { + action: () => + { try { var stageDirectory = _store.GetRandomStagingDirectory(); Directory.CreateDirectory(stageDirectory.Value); rollbackDirectory = stageDirectory.Value; - var tempProject = CreateTempProject( + string tempProject = CreateDirectoryWithTempProject( packageId: packageId, versionRange: versionRange, - targetFramework: string.IsNullOrEmpty(targetFramework) ? BundledTargetFramework.GetTargetFrameworkMoniker() : targetFramework, + targetFramework: string.IsNullOrEmpty(targetFramework) ? BundledTargetFramework.GetTargetFrameworkMoniker() : targetFramework, restoreDirectory: stageDirectory, assetJsonOutputDirectory: stageDirectory, rootConfigDirectory: packageLocation.RootConfigDirectory, @@ -65,13 +65,13 @@ public IToolPackage InstallPackage( try { _projectRestorer.Restore( - tempProject, + new FilePath(tempProject), packageLocation, verbosity: verbosity); } finally { - File.Delete(tempProject.Value); + File.Delete(tempProject); } var version = _store.GetStagedPackageVersion(stageDirectory, packageId); @@ -104,7 +104,8 @@ public IToolPackage InstallPackage( ex); } }, - rollback: () => { + rollback: () => + { if (!string.IsNullOrEmpty(rollbackDirectory) && Directory.Exists(rollbackDirectory)) { Directory.Delete(rollbackDirectory, true); @@ -126,16 +127,13 @@ public IToolPackage InstallPackageToExternalManagedLocation( string targetFramework = null, string verbosity = null) { - var tempDirectoryForAssetJson = new DirectoryPath(Path.GetTempPath()) - .WithSubDirectories(Path.GetRandomFileName()); - - Directory.CreateDirectory(tempDirectoryForAssetJson.Value); + var tempDirectoryForAssetJson = PathUtilities.CreateTempSubdirectory(); - var tempProject = CreateTempProject( + string tempProject = CreateDirectoryWithTempProject( packageId: packageId, versionRange: versionRange, targetFramework: string.IsNullOrEmpty(targetFramework) ? BundledTargetFramework.GetTargetFrameworkMoniker() : targetFramework, - assetJsonOutputDirectory: tempDirectoryForAssetJson, + assetJsonOutputDirectory: new DirectoryPath(tempDirectoryForAssetJson), restoreDirectory: null, rootConfigDirectory: packageLocation.RootConfigDirectory, additionalFeeds: packageLocation.AdditionalFeeds); @@ -143,19 +141,19 @@ public IToolPackage InstallPackageToExternalManagedLocation( try { _projectRestorer.Restore( - tempProject, + new FilePath(tempProject), packageLocation, verbosity: verbosity); } finally { - File.Delete(tempProject.Value); + File.Delete(tempProject); } - return ToolPackageInstance.CreateFromAssetFile(packageId, tempDirectoryForAssetJson); + return ToolPackageInstance.CreateFromAssetFile(packageId, new DirectoryPath(tempDirectoryForAssetJson)); } - private FilePath CreateTempProject( + private string CreateDirectoryWithTempProject( PackageId packageId, VersionRange versionRange, string targetFramework, @@ -164,16 +162,14 @@ private FilePath CreateTempProject( DirectoryPath? rootConfigDirectory, string[] additionalFeeds) { - var tempProject = _tempProject ?? new DirectoryPath(Path.GetTempPath()) - .WithSubDirectories(Path.GetRandomFileName()) - .WithFile("restore.csproj"); - - if (Path.GetExtension(tempProject.Value) != "csproj") + string tempProject; + if (_tempProject != null && _tempProject.HasValue) { - tempProject = new FilePath(Path.ChangeExtension(tempProject.Value, "csproj")); + tempProject = _tempProject.Value.Value; + Directory.CreateDirectory(Path.GetDirectoryName(tempProject)); } - - Directory.CreateDirectory(tempProject.GetDirectoryPath().Value); + else + tempProject = Path.Combine(PathUtilities.CreateTempSubdirectory(), "restore.csproj"); var tempProjectContent = new XDocument( new XElement("Project", @@ -203,7 +199,7 @@ private FilePath CreateTempProject( new XAttribute("Project", "Sdk.targets"), new XAttribute("Sdk", "Microsoft.NET.Sdk")))); - File.WriteAllText(tempProject.Value, tempProjectContent.ToString()); + File.WriteAllText(tempProject, tempProjectContent.ToString()); return tempProject; } diff --git a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs index d8df4fb955aa..52c7432fb321 100644 --- a/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs +++ b/src/Cli/dotnet/commands/InstallingWorkloadCommand.cs @@ -5,10 +5,8 @@ using System; using System.Collections.Generic; using System.CommandLine; -using System.CommandLine.Parsing; using System.IO; using System.Linq; -using System.Text; using System.Threading.Tasks; using Microsoft.Deployment.DotNet.Releases; using Microsoft.DotNet.Cli; @@ -17,7 +15,6 @@ using Microsoft.DotNet.Configurer; using Microsoft.DotNet.ToolPackage; using Microsoft.DotNet.Workloads.Workload.Install; -using Microsoft.DotNet.Workloads.Workload.Install.InstallRecord; using Microsoft.Extensions.EnvironmentAbstractions; using Microsoft.NET.Sdk.WorkloadManifestReader; using NuGet.Versioning; @@ -79,7 +76,7 @@ public InstallingWorkloadCommand( var sourceOption = parseResult.GetValueForOption(InstallingWorkloadCommandParser.SourceOption); _packageSourceLocation = string.IsNullOrEmpty(configOption) && (sourceOption == null || !sourceOption.Any()) ? null : new PackageSourceLocation(string.IsNullOrEmpty(configOption) ? null : new FilePath(configOption), sourceFeedOverrides: sourceOption); - + var sdkWorkloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(_dotnetPath, _installedSdkVersion.ToString(), userProfileDir); _workloadResolver = workloadResolver ?? WorkloadResolver.Create(sdkWorkloadManifestProvider, _dotnetPath, _installedSdkVersion.ToString(), _userProfileDir); diff --git a/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs b/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs index 7e06aae182b1..0b4c199f1b3e 100644 --- a/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-tool/install/ToolInstallGlobalOrToolPathCommand.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.CommandLine; -using System.CommandLine.Parsing; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -72,7 +71,7 @@ public ToolInstallGlobalOrToolPathCommand( _environmentPathInstruction = environmentPathInstruction ?? EnvironmentPathFactory.CreateEnvironmentPathInstruction(); _createShellShimRepository = createShellShimRepository ?? ShellShimRepositoryFactory.CreateShellShimRepository; - var tempDir = new DirectoryPath(Path.Combine(Path.GetTempPath(), "dotnet-tool-install")); + var tempDir = new DirectoryPath(PathUtilities.CreateTempSubdirectory()); var configOption = parseResult.GetValueForOption(ToolInstallCommandParser.ConfigOption); var sourceOption = parseResult.GetValueForOption(ToolInstallCommandParser.AddSourceOption); var packageSourceLocation = new PackageSourceLocation(string.IsNullOrEmpty(configOption) ? null : new FilePath(configOption), additionalSourceFeeds: sourceOption); @@ -143,7 +142,7 @@ public override int Execute() } else { - framework = string.IsNullOrEmpty(_framework) ? + framework = string.IsNullOrEmpty(_framework) ? null : NuGetFramework.Parse(_framework); } diff --git a/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandBase.cs b/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandBase.cs index e0ca3d540524..dc4cd2d3f51d 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandBase.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/WorkloadCommandBase.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System.CommandLine; -using System.CommandLine.Parsing; using System.IO; using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.NuGetPackageDownloader; @@ -104,7 +103,7 @@ public WorkloadCommandBase(ParseResult parseResult, ? tempDirPath : !string.IsNullOrWhiteSpace(parseResult.GetValueForOption(WorkloadInstallCommandParser.TempDirOption)) ? parseResult.GetValueForOption(WorkloadInstallCommandParser.TempDirOption) - : Path.GetTempPath(); + : PathUtilities.CreateTempSubdirectory(); TempPackagesDirectory = new DirectoryPath(Path.Combine(TempDirectoryPath, "dotnet-sdk-advertising-temp")); diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs b/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs index 956ed4bbec1f..245c526076f6 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/FileBasedInstaller.cs @@ -19,6 +19,7 @@ using System.Text.Json; using System.Threading.Tasks; + namespace Microsoft.DotNet.Workloads.Workload.Install { internal class FileBasedInstaller : IInstaller @@ -51,7 +52,7 @@ public FileBasedInstaller(IReporter reporter, { _userProfileDir = userProfileDir; _dotnetDir = dotnetDir ?? Path.GetDirectoryName(Environment.ProcessPath); - _tempPackagesDir = new DirectoryPath(tempDirPath ?? Path.GetTempPath()); + _tempPackagesDir = new DirectoryPath(tempDirPath ?? PathUtilities.CreateTempSubdirectory()); ILogger logger = verbosity.IsDetailedOrDiagnostic() ? new NuGetConsoleLogger() : new NullLogger(); _restoreActionConfig = restoreActionConfig; _nugetPackageDownloader = nugetPackageDownloader ?? @@ -188,7 +189,7 @@ public void InstallWorkloads(IEnumerable workloadIds, SdkFeatureBand Directory.Delete(dir, true); } } - }); + }); } } diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs index ed04a4548648..368d4716b184 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/NetSdkMsiInstallerClient.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; using System.Runtime.InteropServices; @@ -116,7 +115,7 @@ public void GarbageCollectInstalledWorkloadPacks(DirectoryPath? offlineCache = n Log?.LogMessage("Starting garbage collection."); IEnumerable installedFeatureBands = GetInstalledFeatureBands(); IEnumerable installedWorkloads = RecordRepository.GetInstalledWorkloads(_sdkFeatureBand); - Dictionary<(WorkloadPackId id, string version),PackInfo> expectedWorkloadPacks = installedWorkloads + Dictionary<(WorkloadPackId id, string version), PackInfo> expectedWorkloadPacks = installedWorkloads .SelectMany(workload => _workloadResolver.GetPacksInWorkload(workload)) .Distinct() .Select(pack => _workloadResolver.TryGetPackInfo(pack)) @@ -415,9 +414,9 @@ public void InstallWorkloads(IEnumerable workloadIds, SdkFeatureBand RollBackMsiInstall(msiToInstall); } }); - + } - + } void RollBackMsiInstall(WorkloadDownload msiToRollback, DirectoryPath? offlineCache = null) @@ -492,17 +491,10 @@ public PackageId GetManifestPackageId(ManifestId manifestId, SdkFeatureBand feat public async Task ExtractManifestAsync(string nupkgPath, string targetPath) { Log?.LogMessage($"ExtractManifestAsync: Extracting '{nupkgPath}' to '{targetPath}'"); - - string extractionPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); - if (Directory.Exists(extractionPath)) - { - Directory.Delete(extractionPath, true); - } + string extractionPath = PathUtilities.CreateTempSubdirectory(); try { - Directory.CreateDirectory(extractionPath); - Log?.LogMessage($"ExtractManifestAsync: Temporary extraction path: '{extractionPath}'"); await _nugetPackageDownloader.ExtractPackageAsync(nupkgPath, new DirectoryPath(extractionPath)); if (Directory.Exists(targetPath)) @@ -959,7 +951,7 @@ public static NetSdkMsiInstallerClient Create( if (nugetPackageDownloader == null) { - DirectoryPath tempPackagesDir = new(string.IsNullOrWhiteSpace(tempDirPath) ? Path.GetTempPath() : tempDirPath); + DirectoryPath tempPackagesDir = new(string.IsNullOrWhiteSpace(tempDirPath) ? PathUtilities.CreateTempSubdirectory() : tempDirPath); nugetPackageDownloader = new NuGetPackageDownloader(tempPackagesDir, filePermissionSetter: null, new FirstPartyNuGetPackageSigningVerifier(), diff --git a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs index 048d3dc22889..36c42b47cee4 100644 --- a/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs +++ b/src/Cli/dotnet/commands/dotnet-workload/install/WorkloadManifestUpdater.cs @@ -6,7 +6,6 @@ using System.IO; using System.Linq; using System.Net.Http; -using System.Runtime.InteropServices; using System.Text.Json; using System.Threading.Tasks; using Microsoft.DotNet.Cli; @@ -69,7 +68,7 @@ private static WorkloadManifestUpdater GetInstance(string userProfileDir) var sdkVersion = Product.Version; var workloadManifestProvider = new SdkDirectoryWorkloadManifestProvider(dotnetPath, sdkVersion, userProfileDir); var workloadResolver = WorkloadResolver.Create(workloadManifestProvider, dotnetPath, sdkVersion, userProfileDir); - var tempPackagesDir = new DirectoryPath(Path.Combine(Path.GetTempPath(), "dotnet-sdk-advertising-temp")); + var tempPackagesDir = new DirectoryPath(PathUtilities.CreateTempSubdirectory()); var nugetPackageDownloader = new NuGetPackageDownloader(tempPackagesDir, filePermissionSetter: null, new FirstPartyNuGetPackageSigningVerifier(), @@ -189,9 +188,9 @@ Dictionary Workloads } if (advertisingManifestVersionAndWorkloads != null && - ((advertisingManifestVersionAndWorkloads.Value.ManifestVersion.CompareTo(currentManifestVersion.manifestVersion) > 0 + ((advertisingManifestVersionAndWorkloads.Value.ManifestVersion.CompareTo(currentManifestVersion.manifestVersion) > 0 && advertisingManifestVersionAndWorkloads.Value.ManifestFeatureBand.Equals(currentManifestVersion.sdkFeatureBand)) || - advertisingManifestVersionAndWorkloads.Value.ManifestFeatureBand.CompareTo(currentManifestVersion.sdkFeatureBand) > 0)) + advertisingManifestVersionAndWorkloads.Value.ManifestFeatureBand.CompareTo(currentManifestVersion.sdkFeatureBand) > 0)) { manifestUpdates.Add((new ManifestVersionUpdate(manifestId, currentManifestVersion.manifestVersion, currentManifestVersion.sdkFeatureBand.ToString(), advertisingManifestVersionAndWorkloads.Value.ManifestVersion, advertisingManifestVersionAndWorkloads.Value.ManifestFeatureBand.ToString()), @@ -262,7 +261,7 @@ public async Task> GetManifestPackageDownloadsAsyn var newPackageId = _workloadManifestInstaller.GetManifestPackageId(new ManifestId(manifest.Id), installedSdkFeatureBand); (success, latestVersion) = await GetPackageVersion(newPackageId, packageSourceLocation: _packageSourceLocation, includePreview: includePreviews); - + if (success) { downloads.Add(new WorkloadDownload(manifest.Id, newPackageId.ToString(), latestVersion.ToString())); @@ -307,7 +306,7 @@ private async Task UpdateAdvertisingManifestAsync(WorkloadManifestInfo manifest, try { var adManifestPath = GetAdvertisingManifestPath(_sdkFeatureBand, manifestId); - + bool success; (success, packagePath) = await GetManifestPackageUpdate(_sdkFeatureBand, manifestId, includePreviews, offlineCache); if (!success) @@ -323,7 +322,7 @@ private async Task UpdateAdvertisingManifestAsync(WorkloadManifestInfo manifest, _reporter.WriteLine(string.Format(LocalizableStrings.AdManifestPackageDoesNotExist, manifestId)); return; } - + await _workloadManifestInstaller.ExtractManifestAsync(packagePath, adManifestPath); // add file that contains the advertisted manifest feature band so GetAdvertisingManifestVersionAndWorkloads will use correct feature band, regardless of if rollback occurred or not @@ -384,7 +383,7 @@ private async Task UpdateAdvertisingManifestAsync(WorkloadManifestInfo manifest, { adManifestFeatureBand = new SdkFeatureBand(File.ReadAllText(adManifestFeatureBandPath)); } - + return (new ManifestVersion(manifest.Version), adManifestFeatureBand, manifest.Workloads.Values.OfType().ToDictionary(w => w.Id)); } @@ -470,12 +469,12 @@ private async Task NewerManifestPackageExists(ManifestId manifest) ManifestVersion manifestVersion; SdkFeatureBand manifestFeatureBand; var parts = manifest.Value.Split('/'); - + string manifestVersionString = (parts[0]); if (!FXVersion.TryParse(manifestVersionString, out FXVersion version)) { throw new FormatException(String.Format(LocalizableStrings.InvalidVersionForWorkload, manifest.Key, manifestVersionString)); - } + } manifestVersion = new ManifestVersion(parts[0]); if (parts.Length == 1) @@ -499,15 +498,15 @@ private bool BackgroundUpdatesAreDisabled() => private static string GetAdvertisingWorkloadsFilePath(string userProfileDir, SdkFeatureBand featureBand) => Path.Combine(userProfileDir, $".workloadAdvertisingUpdates{featureBand}"); - private async Task GetOnlinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId manifestId, bool includePreviews) - { - string packagePath = await _nugetPackageDownloader.DownloadPackageAsync( - _workloadManifestInstaller.GetManifestPackageId(manifestId, sdkFeatureBand), - packageSourceLocation: _packageSourceLocation, - includePreview: includePreviews); - - return packagePath; - } + private async Task GetOnlinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId manifestId, bool includePreviews) + { + string packagePath = await _nugetPackageDownloader.DownloadPackageAsync( + _workloadManifestInstaller.GetManifestPackageId(manifestId, sdkFeatureBand), + packageSourceLocation: _packageSourceLocation, + includePreview: includePreviews); + + return packagePath; + } private string GetOfflinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId manifestId, DirectoryPath? offlineCache = null) { @@ -527,7 +526,7 @@ private string GetOfflinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId m { if (offlineCache == null || !offlineCache.HasValue) { - try + try { string packagePath = await GetOnlinePackagePath(sdkFeatureBand, manifestId, includePreviews); return (true, packagePath); @@ -557,9 +556,9 @@ private string GetOfflinePackagePath(SdkFeatureBand sdkFeatureBand, ManifestId m } } - -private string GetAdvertisingManifestPath(SdkFeatureBand featureBand, ManifestId manifestId) => - Path.Combine(_userProfileDir, "sdk-advertising", featureBand.ToString(), manifestId.ToString()); + + private string GetAdvertisingManifestPath(SdkFeatureBand featureBand, ManifestId manifestId) => + Path.Combine(_userProfileDir, "sdk-advertising", featureBand.ToString(), manifestId.ToString()); } } diff --git a/src/Cli/dotnet/dotnet.csproj b/src/Cli/dotnet/dotnet.csproj index 3c207d9c173d..849ad24e8c65 100644 --- a/src/Cli/dotnet/dotnet.csproj +++ b/src/Cli/dotnet/dotnet.csproj @@ -95,8 +95,7 @@ - + diff --git a/src/Common/CompileOptions.cs b/src/Common/CompileOptions.cs index 2ba77d957d5e..2df7fecc6221 100644 --- a/src/Common/CompileOptions.cs +++ b/src/Common/CompileOptions.cs @@ -1,5 +1,10 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using System.Reflection; + +#if MICROSOFT_ENABLE_TELEMETRY + [assembly: AssemblyMetadata("TelemetryOptOutDefault", Microsoft.DotNet.Cli.CompileOptions.TelemetryOptOutDefaultString)] +#endif namespace Microsoft.DotNet.Cli { @@ -10,6 +15,12 @@ static class CompileOptions false; #else true; +#endif + public const string TelemetryOptOutDefaultString = +#if MICROSOFT_ENABLE_TELEMETRY + "False"; +#else + "True"; #endif } } diff --git a/src/Common/PathUtilities.cs b/src/Common/PathUtilities.cs new file mode 100644 index 000000000000..dad9351b1ede --- /dev/null +++ b/src/Common/PathUtilities.cs @@ -0,0 +1,49 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using System.IO; +using System.Runtime.InteropServices; + +namespace Microsoft.DotNet; + +static class PathUtilities +{ + const int S_IRUSR = 256; + const int S_IWUSR = 128; + const int S_IXUSR = 64; + const int S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR; // 700 (octal) Permissions + + const int MAX_NUM_DIRECTORY_CREATE_RETRIES = 2; + + public static string CreateTempSubdirectory() + { + return CreateTempSubdirectoryRetry(0); + } + + [DllImport("libc", SetLastError = true)] + private static extern int mkdir(string pathname, int mode); + private static string CreateTempSubdirectoryRetry(int attemptNo) + { + string path = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString()); + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + int mkdirStatusCode = mkdir(path, S_IRWXU); + if (mkdirStatusCode != 0) + { + int errno = Marshal.GetLastWin32Error(); + if (Directory.Exists(path) && attemptNo < MAX_NUM_DIRECTORY_CREATE_RETRIES) + { + return CreateTempSubdirectoryRetry(attemptNo + 1); + } + else + throw new IOException($"Failed to create a temporary subdirectory {path} with mkdir, error code: {errno}"); + } + } + else + { + Directory.CreateDirectory(path); + } + return path; + } +} diff --git a/src/Layout/redist/trustedroots/codesignctl.pem b/src/Layout/redist/trustedroots/codesignctl.pem index efa36732f919..a973427758b9 100644 --- a/src/Layout/redist/trustedroots/codesignctl.pem +++ b/src/Layout/redist/trustedroots/codesignctl.pem @@ -8384,6 +8384,22 @@ CwqQAjBdGuxRidRk3PnlHji9Wy7j5UTkOxh61/CVQI/y68/0+dBlokHysOZ8wTYs j1453Tc= -----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV +BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk +LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv +b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ +BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg +THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v +IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv +xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H +Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB +eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo +jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ ++efcMQ== +-----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- MIIFtjCCA56gAwIBAgIQFcKuKk2ZmmOM07oTGXYI9TANBgkqhkiG9w0BAQsFADB1 MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl @@ -8468,3 +8484,37 @@ Ts3Xroud2xb9BMaSvdSI5qmjqrv3ZDg7X8wM0DW+dBkDpsWqTKJhNoI+HfMrvJdd 20t4Oy31O+9gI+j17AsjNpWvmGa/U9N7uGlKKpZmacSUxvRfbqyYeIiABlyisu2i -----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFojCCA4qgAwIBAgIUf/Go+fQ66IduLcb/XkM9su4wpkMwDQYJKoZIhvcNAQEN +BQAwaTELMAkGA1UEBhMCVVMxFjAUBgNVBAoMDUVudHJ1c3QsIEluYy4xQjBABgNV +BAMMOUVudHJ1c3QgQ29kZSBTaWduaW5nIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkgLSBDU0JSMTAeFw0yMTA1MDcxMzI2MzZaFw00MDEyMzAxMzI2MzZaMGkx +CzAJBgNVBAYTAlVTMRYwFAYDVQQKDA1FbnRydXN0LCBJbmMuMUIwQAYDVQQDDDlF +bnRydXN0IENvZGUgU2lnbmluZyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gQ1NCUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCngY/3FEW2 +YkPy2K7TJV5IT1G/xX2fUBw10dZ+YSqUGW0nRqSmGl33VFFqgCLGqGZ1TVSDyV5o +G6v2W2Swra0gvVTvRmttAudFrnX2joq5Mi6LuHccUk15iF+lOhjJUCyXJy2/2gB9 +Y3/vMuxGh2Pbmp/DWiE2e/mb1cqgbnIs/OHxnnBNCFYVb5Cr+0i6udfBgniFZS5/ +tcnA4hS3NxFBBuKK4Kj25X62eAUBw2DtTwdBLgoTSeOQm3/dvfqsv2RR0VybtPVc +51z/O5uloBrXfQmywrf/bhy8yH3m6Sv8crMU6UpVEoScRCV1HfYq8E+lID1oJeth +l3wP5bY9867DwRG8G47M4EcwXkIAhnHjWKwGymUfe5SmS1dnDH5erXhnW1XjXuvH +2OxMbobL89z4n4eqclgSD32m+PhCOTs8LOQyTUmM4OEAwjignPqEPkHcblauxhpb +9GdoBQHNG7+uh7ydU/Yu6LZr5JnexU+HWKjSZR7IH9Vybu5ZHFc7CXKd18q3kMbN +e0WSkUIDTH0/yvKquMIOhvMQn0YupGaGaFpoGHApOBGAYGuKQ6NzbOOzazf/5p1n +AZKG3y9I0ftQYNVc/iHTAUJj/u9wtBfAj6ju08FLXxLq/f0uDodEYOOp9MIYo+P9 +zgyEIg3zp3jak/PbOM+5LzPG/wc8Xr5F0wIDAQABo0IwQDAdBgNVHQ4EFgQUgrrW +PZfOn89x6JI3r/2ztWk1V88wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwDQYJKoZIhvcNAQENBQADggIBABLvOKGI4aGj1mXcR5zzvNzPrEuMPBq+K/T3 +0GcXaIZNcKjyzdAxAld9qQyKO1c5nvBu9yQiBWfRwZbBvtHw+FZnC96614ibjddr +CHb1WJHZtcNAUxqk8YXNPwBOP06TO3i50gdSZAyGaW3oVGVWF+gAU4SK89v7s84L +VWKPzUxJBjh/UsPzHNc99zPKMq3Bqa9v6xHL7qxRv7AmmnpOI7RK9mm0QmnWoI22 +jEdKOyA3t0EH7y8g2GYcaZeobDB8d0Nea74mmIMPOtbHcCoWRi0lVIZjZVdC9yNB +6VBqB0POTrXpH2jY2NYJSqjosvyQZ5LkkCbzR/rWIPuJgOJEczn3ioYzC/iqqedN +7Nxv1c8xTauOH5BA1nxcgg+uF1Jx6aznTTjtKth2eYetF6NMq7dCV78GrOXQTTDp +VU/jRcrz4GohNI3HnxyjY0iS0pYHvqVHPsIqmTinjtohfFFt3Ms9B+mpvUnUXTVf +W4wEUeqaWJC6G69oeLEWD5QpO4+bKo/JIPBxQkxcTasxjKvpfyZoaaClFg2BxNEF +DMOHZuUHY6obTv+yB0FPpSJGUKxmAIdSbDyyO5yXoUa0W97PwmpZVQeMo6TRdzVn +RgQv2Ti5Rq+6jhtyJgIvdlTvg8IvLHdwzHcQkqoDrcrM4E/pg0blszwZb3p5h7Y4 +mr1CzqRi +-----END CERTIFICATE----- + diff --git a/src/Layout/redist/trustedroots/timestampctl.pem b/src/Layout/redist/trustedroots/timestampctl.pem index 6e6aeb668d77..c04ccb8e7cb1 100644 --- a/src/Layout/redist/trustedroots/timestampctl.pem +++ b/src/Layout/redist/trustedroots/timestampctl.pem @@ -8739,6 +8739,22 @@ CwqQAjBdGuxRidRk3PnlHji9Wy7j5UTkOxh61/CVQI/y68/0+dBlokHysOZ8wTYs j1453Tc= -----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIICQDCCAeWgAwIBAgIMAVRI7yH9l1kN9QQKMAoGCCqGSM49BAMCMHExCzAJBgNV +BAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMgTHRk +LjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25vIFJv +b3QgQ0EgMjAxNzAeFw0xNzA4MjIxMjA3MDZaFw00MjA4MjIxMjA3MDZaMHExCzAJ +BgNVBAYTAkhVMREwDwYDVQQHDAhCdWRhcGVzdDEWMBQGA1UECgwNTWljcm9zZWMg +THRkLjEXMBUGA1UEYQwOVkFUSFUtMjM1ODQ0OTcxHjAcBgNVBAMMFWUtU3ppZ25v +IFJvb3QgQ0EgMjAxNzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJbcPYrYsHtv +xie+RJCxs1YVe45DJH0ahFnuY2iyxl6H0BVIHqiQrb1TotreOpCmYF9oMrWGQd+H +Wyx7xf58etqjYzBhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0G +A1UdDgQWBBSHERUI0arBeAyxr87GyZDvvzAEwDAfBgNVHSMEGDAWgBSHERUI0arB +eAyxr87GyZDvvzAEwDAKBggqhkjOPQQDAgNJADBGAiEAtVfd14pVCzbhhkT61Nlo +jbjcI4qKDdQvfepz7L9NbKgCIQDLpbQS+ue16M9+k/zzNY9vTlp8tLxOsvxyqltZ ++efcMQ== +-----END CERTIFICATE----- + -----BEGIN CERTIFICATE----- MIIFtjCCA56gAwIBAgIQFcKuKk2ZmmOM07oTGXYI9TANBgkqhkiG9w0BAQsFADB1 MQswCQYDVQQGEwJHUjE3MDUGA1UECgwuSGVsbGVuaWMgQWNhZGVtaWMgYW5kIFJl @@ -8857,3 +8873,37 @@ zWIEm5dte7Q3toaZwmAK/G0ZBkI3ZLwXum1LzddoRhN51ltgmc6NzfDIG97qKL7M z4leKEMaJ8dId29zULSpxVPgc8dUrdyac1E= -----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIFojCCA4qgAwIBAgIUf/Go+fQ66IduLcb/XkM9su4wpkMwDQYJKoZIhvcNAQEN +BQAwaTELMAkGA1UEBhMCVVMxFjAUBgNVBAoMDUVudHJ1c3QsIEluYy4xQjBABgNV +BAMMOUVudHJ1c3QgQ29kZSBTaWduaW5nIFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRo +b3JpdHkgLSBDU0JSMTAeFw0yMTA1MDcxMzI2MzZaFw00MDEyMzAxMzI2MzZaMGkx +CzAJBgNVBAYTAlVTMRYwFAYDVQQKDA1FbnRydXN0LCBJbmMuMUIwQAYDVQQDDDlF +bnRydXN0IENvZGUgU2lnbmluZyBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5 +IC0gQ1NCUjEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCngY/3FEW2 +YkPy2K7TJV5IT1G/xX2fUBw10dZ+YSqUGW0nRqSmGl33VFFqgCLGqGZ1TVSDyV5o +G6v2W2Swra0gvVTvRmttAudFrnX2joq5Mi6LuHccUk15iF+lOhjJUCyXJy2/2gB9 +Y3/vMuxGh2Pbmp/DWiE2e/mb1cqgbnIs/OHxnnBNCFYVb5Cr+0i6udfBgniFZS5/ +tcnA4hS3NxFBBuKK4Kj25X62eAUBw2DtTwdBLgoTSeOQm3/dvfqsv2RR0VybtPVc +51z/O5uloBrXfQmywrf/bhy8yH3m6Sv8crMU6UpVEoScRCV1HfYq8E+lID1oJeth +l3wP5bY9867DwRG8G47M4EcwXkIAhnHjWKwGymUfe5SmS1dnDH5erXhnW1XjXuvH +2OxMbobL89z4n4eqclgSD32m+PhCOTs8LOQyTUmM4OEAwjignPqEPkHcblauxhpb +9GdoBQHNG7+uh7ydU/Yu6LZr5JnexU+HWKjSZR7IH9Vybu5ZHFc7CXKd18q3kMbN +e0WSkUIDTH0/yvKquMIOhvMQn0YupGaGaFpoGHApOBGAYGuKQ6NzbOOzazf/5p1n +AZKG3y9I0ftQYNVc/iHTAUJj/u9wtBfAj6ju08FLXxLq/f0uDodEYOOp9MIYo+P9 +zgyEIg3zp3jak/PbOM+5LzPG/wc8Xr5F0wIDAQABo0IwQDAdBgNVHQ4EFgQUgrrW +PZfOn89x6JI3r/2ztWk1V88wDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC +AYYwDQYJKoZIhvcNAQENBQADggIBABLvOKGI4aGj1mXcR5zzvNzPrEuMPBq+K/T3 +0GcXaIZNcKjyzdAxAld9qQyKO1c5nvBu9yQiBWfRwZbBvtHw+FZnC96614ibjddr +CHb1WJHZtcNAUxqk8YXNPwBOP06TO3i50gdSZAyGaW3oVGVWF+gAU4SK89v7s84L +VWKPzUxJBjh/UsPzHNc99zPKMq3Bqa9v6xHL7qxRv7AmmnpOI7RK9mm0QmnWoI22 +jEdKOyA3t0EH7y8g2GYcaZeobDB8d0Nea74mmIMPOtbHcCoWRi0lVIZjZVdC9yNB +6VBqB0POTrXpH2jY2NYJSqjosvyQZ5LkkCbzR/rWIPuJgOJEczn3ioYzC/iqqedN +7Nxv1c8xTauOH5BA1nxcgg+uF1Jx6aznTTjtKth2eYetF6NMq7dCV78GrOXQTTDp +VU/jRcrz4GohNI3HnxyjY0iS0pYHvqVHPsIqmTinjtohfFFt3Ms9B+mpvUnUXTVf +W4wEUeqaWJC6G69oeLEWD5QpO4+bKo/JIPBxQkxcTasxjKvpfyZoaaClFg2BxNEF +DMOHZuUHY6obTv+yB0FPpSJGUKxmAIdSbDyyO5yXoUa0W97PwmpZVQeMo6TRdzVn +RgQv2Ti5Rq+6jhtyJgIvdlTvg8IvLHdwzHcQkqoDrcrM4E/pg0blszwZb3p5h7Y4 +mr1CzqRi +-----END CERTIFICATE----- + diff --git a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs index 5686a4a25ba4..5cd254c1e71f 100644 --- a/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs +++ b/src/Resolvers/Microsoft.DotNet.NativeWrapper/Interop.cs @@ -14,6 +14,9 @@ // Work around https://github.com/dotnet/roslyn-analyzers/issues/6094 #pragma warning disable CA1420 +// Work around https://github.com/dotnet/roslyn-analyzers/issues/6094 +#pragma warning disable CA1420 + namespace Microsoft.DotNet.NativeWrapper { public static partial class Interop diff --git a/src/Tasks/Common/FileUtilities.cs b/src/Tasks/Common/FileUtilities.cs index 92375d98eeba..76f79690f00b 100644 --- a/src/Tasks/Common/FileUtilities.cs +++ b/src/Tasks/Common/FileUtilities.cs @@ -37,6 +37,5 @@ public static Version TryGetAssemblyVersion(string sourcePath) return s_assemblyExtensions.Contains(extension) ? GetAssemblyVersion(sourcePath) : null; } - } } diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAResolvePackageAssetsTask.cs b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAResolvePackageAssetsTask.cs index e0aaa508409c..c75c1fe46a67 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAResolvePackageAssetsTask.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/GivenAResolvePackageAssetsTask.cs @@ -4,7 +4,6 @@ using FluentAssertions; using Microsoft.Build.Framework; using System; -using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; @@ -117,15 +116,11 @@ public void It_does_not_error_on_duplicate_package_names() new CacheWriter(task); // Should not error } - [Fact] - public void It_warns_on_invalid_culture_codes_of_resources() - { - string projectAssetsJsonPath = Path.GetTempFileName(); - var assetsContent = @" + private static string AssetsFileWithInvalidLocale(string tfm, string locale) => @" { `version`: 3, `targets`: { - `net7.0`: { + `{tfm}`: { `JavaScriptEngineSwitcher.Core/3.3.0`: { `type`: `package`, `compile`: { @@ -136,59 +131,65 @@ public void It_warns_on_invalid_culture_codes_of_resources() }, `resource`: { `lib/netstandard2.0/ru-ru/JavaScriptEngineSwitcher.Core.resources.dll`: { - `locale`: `what is this even` + `locale`: `{locale}` } } } } + }, + `project`: { + `version`: `1.0.0`, + `frameworks`: { + `{tfm}`: { + `targetAlias`: `{tfm}` + } + } } -}".Replace("`", "\""); +}".Replace("`", "\"").Replace("{tfm}", tfm).Replace("{locale}", locale); + + [InlineData("net7.0", true)] + [InlineData("net6.0", false)] + [Theory] + public void It_warns_on_invalid_culture_codes_of_resources(string tfm, bool shouldHaveWarnings) + { + string projectAssetsJsonPath = Path.GetTempFileName(); + var assetsContent = AssetsFileWithInvalidLocale(tfm, "what is this even"); File.WriteAllText(projectAssetsJsonPath, assetsContent); var task = InitializeTask(out _); task.ProjectAssetsFile = projectAssetsJsonPath; - task.TargetFramework = "net7.0"; - var writer = new CacheWriter(task); - writer.WriteToCacheFile(); - var messages = (task.BuildEngine as MockBuildEngine).Warnings; - var invalidContextMessages = messages.Where(msg => msg.Code == "NETSDK1188"); - invalidContextMessages.Should().HaveCount(1); + task.TargetFramework = tfm; + var writer = new CacheWriter(task, new MockPackageResolver()); + writer.WriteToMemoryStream(); + var engine = task.BuildEngine as MockBuildEngine; + + var invalidContextWarnings = engine.Warnings.Where(msg => msg.Code == "NETSDK1188"); + invalidContextWarnings.Should().HaveCount(shouldHaveWarnings ? 1 : 0); + + var invalidContextMessages = engine.Messages.Where(msg => msg.Code == "NETSDK1188"); + invalidContextMessages.Should().HaveCount(shouldHaveWarnings ? 0 : 1); + } - - [Fact] - public void It_warns_on_incorrectly_cased_culture_codes_of_resources() + + [InlineData("net7.0", true)] + [InlineData("net6.0", false)] + [Theory] + public void It_warns_on_incorrectly_cased_culture_codes_of_resources(string tfm, bool shouldHaveWarnings) { string projectAssetsJsonPath = Path.GetTempFileName(); - var assetsContent = @" -{ - `version`: 3, - `targets`: { - `net7.0`: { - `JavaScriptEngineSwitcher.Core/3.3.0`: { - `type`: `package`, - `compile`: { - `lib/netstandard2.0/JavaScriptEngineSwitcher.Core.dll`: {} - }, - `runtime`: { - `lib/netstandard2.0/JavaScriptEngineSwitcher.Core.dll`: {} - }, - `resource`: { - `lib/netstandard2.0/ru-ru/JavaScriptEngineSwitcher.Core.resources.dll`: { - `locale`: `ru-ru` - } - } - } - } - } -}".Replace("`", "\""); + var assetsContent = AssetsFileWithInvalidLocale(tfm, "ru-ru"); File.WriteAllText(projectAssetsJsonPath, assetsContent); var task = InitializeTask(out _); task.ProjectAssetsFile = projectAssetsJsonPath; - task.TargetFramework = "net7.0"; - var writer = new CacheWriter(task); - writer.WriteToCacheFile(); - var messages = (task.BuildEngine as MockBuildEngine).Warnings; - var invalidContextMessages = messages.Where(msg => msg.Code == "NETSDK1187"); - invalidContextMessages.Should().HaveCount(1); + task.TargetFramework = tfm; + var writer = new CacheWriter(task, new MockPackageResolver()); + writer.WriteToMemoryStream(); + var engine = task.BuildEngine as MockBuildEngine; + + var invalidContextWarnings = engine.Warnings.Where(msg => msg.Code == "NETSDK1187"); + invalidContextWarnings.Should().HaveCount(shouldHaveWarnings ? 1 : 0); + + var invalidContextMessages = engine.Messages.Where(msg => msg.Code == "NETSDK1187"); + invalidContextMessages.Should().HaveCount(shouldHaveWarnings ? 0 : 1); } private ResolvePackageAssets InitializeTask(out IEnumerable inputProperties) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Mocks/MockPackageResolver.cs b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Mocks/MockPackageResolver.cs index 85ccb9da17c2..1945185459ba 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Mocks/MockPackageResolver.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/Mocks/MockPackageResolver.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using NuGet.ProjectModel; using NuGet.Versioning; using System.IO; @@ -24,5 +25,7 @@ public string GetPackageDirectory(string packageId, NuGetVersion version, out st packageRoot = _root; return Path.Combine(_root, packageId, version.ToNormalizedString(), "path"); } + + public string ResolvePackageAssetPath(LockFileTargetLibrary package, string relativePath) => Path.Combine(GetPackageDirectory(package.Name, package.Version), relativePath); } -} \ No newline at end of file +} diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/IPackageResolver.cs b/src/Tasks/Microsoft.NET.Build.Tasks/IPackageResolver.cs index 1540f00e5089..e208ba1bcfca 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/IPackageResolver.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/IPackageResolver.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. +using NuGet.ProjectModel; using NuGet.Versioning; namespace Microsoft.NET.Build.Tasks @@ -9,5 +10,6 @@ internal interface IPackageResolver { string GetPackageDirectory(string packageId, NuGetVersion version); string GetPackageDirectory(string packageId, NuGetVersion version, out string packageRoot); + string ResolvePackageAssetPath(LockFileTargetLibrary package, string relativePath); } } diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs index 4ee121675e0c..48ee5d280c58 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ResolvePackageAssets.cs @@ -657,8 +657,8 @@ internal sealed class CacheWriter : IDisposable private ResolvePackageAssets _task; private BinaryWriter _writer; private LockFile _lockFile; - private NuGetPackageResolver _packageResolver; private LockFileTarget _compileTimeTarget; + private IPackageResolver _packageResolver; private LockFileTarget _runtimeTarget; private Dictionary _stringTable; private List _metadataStrings; @@ -677,6 +677,15 @@ internal sealed class CacheWriter : IDisposable private const char RelatedPropertySeparator = ';'; + /// + /// This constructor should only be used for testing - IPackgeResolver carries a lot of + /// state so using mocks really help with testing this component. + /// + public CacheWriter(ResolvePackageAssets task, IPackageResolver resolver) : this(task) + { + _packageResolver = resolver; + } + public CacheWriter(ResolvePackageAssets task) { _targetFramework = task.TargetFramework; @@ -1425,14 +1434,30 @@ private void WriteResourceAssemblies() var normalizedLocale = System.Globalization.CultureInfo.GetCultureInfo(locale).Name; if (normalizedLocale != locale) { - _task.Log.LogWarning(Strings.PackageContainsIncorrectlyCasedLocale, package.Name, package.Version.ToNormalizedString(), locale, normalizedLocale); + var tfm = _lockFile.GetTargetAndThrowIfNotFound(_targetFramework, null).TargetFramework; + if (tfm.Version.Major >= 7) + { + _task.Log.LogWarning(Strings.PackageContainsIncorrectlyCasedLocale, package.Name, package.Version.ToNormalizedString(), locale, normalizedLocale); + } + else + { + _task.Log.LogMessage(Strings.PackageContainsIncorrectlyCasedLocale, package.Name, package.Version.ToNormalizedString(), locale, normalizedLocale); + } } locale = normalizedLocale; } catch (System.Globalization.CultureNotFoundException cnf) { - _task.Log.LogWarning(Strings.PackageContainsUnknownLocale, package.Name, package.Version.ToNormalizedString(), cnf.InvalidCultureName); - // We could potentially strip this unknown locales at this point, but we do not. + var tfm = _lockFile.GetTargetAndThrowIfNotFound(_targetFramework, null).TargetFramework; + if (tfm.Version.Major >= 7) + { + _task.Log.LogWarning(Strings.PackageContainsUnknownLocale, package.Name, package.Version.ToNormalizedString(), cnf.InvalidCultureName); + } else + { + _task.Log.LogMessage(Strings.PackageContainsUnknownLocale, package.Name, package.Version.ToNormalizedString(), cnf.InvalidCultureName); + } + + // We could potentially strip this unknown locale at this point, but we do not. // Locale data can change over time (it's typically an OS database that's kept updated), // and the data on the system running the build may not be the same data as // the system executing the built code. So we should be permissive for this case. diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.EolTargetFrameworks.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.EolTargetFrameworks.targets index 38e6b8697121..c9c840088206 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.EolTargetFrameworks.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.EolTargetFrameworks.targets @@ -20,7 +20,7 @@ Copyright (c) .NET Foundation. All rights reserved. receive servicing updates and security fixes. --> - <_EolNetCoreTargetFrameworkVersions Include="1.0;1.1;2.0;2.1;2.2;3.0" /> + <_EolNetCoreTargetFrameworkVersions Include="1.0;1.1;2.0;2.1;2.2;3.0;5.0" /> int:[T:CompatTests.FooAttribute]"), CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.F->int:[T:CompatTests.BarAttribute]"), - CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.F->int:[T:CompatTests.BazAttribute]") } }, // Attributes on method parameter @@ -620,7 +653,6 @@ public void F([Baz] int v, [Foo(""T"")] string s) {} ", new CompatDifference[] { CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.F(System.Int32,System.String)$0:[T:CompatTests.BarAttribute]"), - CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.F(System.Int32,System.String)$0:[T:CompatTests.BazAttribute]"), CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "M:CompatTests.First.F(System.Int32,System.String)$1:[T:CompatTests.FooAttribute]"), } @@ -671,7 +703,6 @@ public class First<[Baz] T1, [Foo(""T"")] T2> {} ", new CompatDifference[] { CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "T:CompatTests.First`2<0>:[T:CompatTests.BarAttribute]"), - CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "T:CompatTests.First`2<0>:[T:CompatTests.BazAttribute]"), CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "T:CompatTests.First`2<1>:[T:CompatTests.FooAttribute]"), } @@ -728,25 +759,594 @@ public class First { ", new CompatDifference[] { CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.F``2<0>:[T:CompatTests.BarAttribute]"), - CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.F``2<0>:[T:CompatTests.BazAttribute]"), CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "M:CompatTests.First.F``2<1>:[T:CompatTests.FooAttribute]"), } } }; - [Theory] - [MemberData(nameof(TypesCases))] - [MemberData(nameof(MembersCases))] - public void EnsureDiagnosticIsReported(string leftSyntax, string rightSyntax, CompatDifference[] expected) + public static TheoryData StrictMode => new() { - using TempDirectory root = new(); - string filePath = Path.Combine(root.DirPath, "exclusions.txt"); - File.Create(filePath).Dispose(); - TestRuleFactory s_ruleFactory = new((settings, context) => new AttributesMustMatch(settings, context, new[] { filePath })); - IAssemblySymbol left = SymbolFactory.GetAssemblyFromSyntax(leftSyntax); - IAssemblySymbol right = SymbolFactory.GetAssemblyFromSyntax(rightSyntax); - ApiComparer differ = new(s_ruleFactory); + // Attribute added to type + { + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A; + public int B; + } + + [Foo(""S"", A = true, B = 3)] + public class First {} +} +", + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A; + public int B; + } + + [Serializable] + [Foo(""S"", A = true, B = 3)] + public class First {} +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "T:CompatTests.First:[T:System.SerializableAttribute]") +} + }, + // Attribute repeated with additional arguments + { + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A; + public int B; + } + + [Serializable] + [Foo(""S"")] + public class First {} +} +", + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A; + public int B; + } + + [Serializable] + [Foo(""S"")] + [Foo(""T"")] + public class First {} +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "T:CompatTests.First:[T:CompatTests.FooAttribute]") +} + }, + // Attributes on method + { + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A; + public int B; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First { + + [Foo(""S"", A = true, B = 3)] + [Bar] + public void F() {} + } +} +", + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A = false; + public int B = 0; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First { + + [Foo(""T"")] + [Baz] + public void F() {} + } +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "M:CompatTests.First.F:[T:CompatTests.FooAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.F:[T:CompatTests.BarAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.F:[T:CompatTests.BazAttribute]") +} + }, + // Attributes on property + { + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A; + public int B; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First { + + [Foo(""S"", A = true, B = 3)] + [Bar] + public int F { get; } + } +} +", + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A = false; + public int B = 0; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First { + + [Foo(""T"")] + [Baz] + public int F { get; } + } +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "P:CompatTests.First.F:[T:CompatTests.FooAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "P:CompatTests.First.F:[T:CompatTests.BarAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "P:CompatTests.First.F:[T:CompatTests.BazAttribute]") +} + }, + // Attributes on event + { + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A; + public int B; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First { + + public delegate void EventHandler(object sender, object e); + + [Foo(""S"", A = true, B = 3)] + [Bar] + public event EventHandler F; + } +} +", + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A = false; + public int B = 0; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First { + + public delegate void EventHandler(object sender, object e); + + [Foo(""T"")] + [Baz] + public event EventHandler F; + } +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "E:CompatTests.First.F:[T:CompatTests.FooAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "E:CompatTests.First.F:[T:CompatTests.BarAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "E:CompatTests.First.F:[T:CompatTests.BazAttribute]") +} + }, + // Attributes on constructor + { + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A; + public int B; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First { + + [Foo(""S"", A = true, B = 3)] + [Bar] + public First() {} + } +} +", + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A = false; + public int B = 0; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First { + + [Foo(""T"")] + [Baz] + public First() {} + } +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "M:CompatTests.First.#ctor:[T:CompatTests.FooAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.#ctor:[T:CompatTests.BarAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.#ctor:[T:CompatTests.BazAttribute]") +} + }, + // Attributes on return type + { + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A; + public int B; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First { + + [return: Foo(""S"", A = true, B = 3)] + [return: Bar] + public int F() => 0; + } +} +", + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A = false; + public int B = 0; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First { + + [return: Foo(""T"")] + [return: Baz] + public int F() => 0; + } +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "M:CompatTests.First.F->int:[T:CompatTests.FooAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.F->int:[T:CompatTests.BarAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.F->int:[T:CompatTests.BazAttribute]") +} + }, + // Attributes on method parameter + { + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A = false; + public int B = 0; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First { + + public void F([Bar] int v, [Foo(""S"", A = true, B = 0)] string s) {} + } +} +", + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A = false; + public int B = 0; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First { + + public void F([Baz] int v, [Foo(""T"")] string s) {} + } +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.F(System.Int32,System.String)$0:[T:CompatTests.BarAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.F(System.Int32,System.String)$0:[T:CompatTests.BazAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "M:CompatTests.First.F(System.Int32,System.String)$1:[T:CompatTests.FooAttribute]"), + +} + }, + // Attributes on type parameter of class + { + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A = false; + public int B = 0; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First<[Bar] T1, [Foo(""S"", A = true, B = 0)] T2> {} +} +", + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A = false; + public int B = 0; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First<[Baz] T1, [Foo(""T"")] T2> {} +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "T:CompatTests.First`2<0>:[T:CompatTests.BarAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "T:CompatTests.First`2<0>:[T:CompatTests.BazAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "T:CompatTests.First`2<1>:[T:CompatTests.FooAttribute]"), + +} + }, + // Attributes on type parameter of method + { + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A = false; + public int B = 0; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First { + + public void F<[Bar] T1, [Foo(""S"", A = true, B = 0)] T2>() {} + } +} +", + @" +namespace CompatTests +{ + using System; + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class FooAttribute : Attribute { + public FooAttribute(String s) {} + public bool A = false; + public int B = 0; + } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BarAttribute : Attribute { } + + [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] + public class BazAttribute : Attribute { } + + public class First { + + public void F<[Baz] T1, [Foo(""T"")] T2>() {} + } +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotRemoveAttribute, "", DifferenceType.Removed, "M:CompatTests.First.F``2<0>:[T:CompatTests.BarAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotAddAttribute, "", DifferenceType.Added, "M:CompatTests.First.F``2<0>:[T:CompatTests.BazAttribute]"), + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotChangeAttribute, "", DifferenceType.Changed, "M:CompatTests.First.F``2<1>:[T:CompatTests.FooAttribute]"), + +} + } + }; + + [Theory] + [MemberData(nameof(TypesCases))] + [MemberData(nameof(MembersCases))] + public void EnsureDiagnosticIsReported(string leftSyntax, string rightSyntax, CompatDifference[] expected) + { + using TempDirectory root = new(); + string filePath = Path.Combine(root.DirPath, "exclusions.txt"); + File.Create(filePath).Dispose(); + TestRuleFactory s_ruleFactory = new((settings, context) => new AttributesMustMatch(settings, context, new[] { filePath })); + IAssemblySymbol left = SymbolFactory.GetAssemblyFromSyntax(leftSyntax); + IAssemblySymbol right = SymbolFactory.GetAssemblyFromSyntax(rightSyntax); + ApiComparer differ = new(s_ruleFactory); + + IEnumerable actual = differ.GetDifferences(left, right); + + Assert.Equal(expected, actual); + } + + [Theory] + [MemberData(nameof(StrictMode))] + public void EnsureStrictModeReported(string leftSyntax, string rightSyntax, CompatDifference[] expected) + { + using TempDirectory root = new(); + string filePath = Path.Combine(root.DirPath, "exclusions.txt"); + File.Create(filePath).Dispose(); + TestRuleFactory s_ruleFactory = new((settings, context) => new AttributesMustMatch(settings, context, new[] { filePath })); + IAssemblySymbol left = SymbolFactory.GetAssemblyFromSyntax(leftSyntax); + IAssemblySymbol right = SymbolFactory.GetAssemblyFromSyntax(rightSyntax); + ApiComparer differ = new(s_ruleFactory, new ApiComparerSettings(strictMode: true)); IEnumerable actual = differ.GetDifferences(left, right); diff --git a/src/Tests/Microsoft.DotNet.ApiCompatibility.Tests/Rules/CannotAddOrRemoveVirtualKeywordTests.cs b/src/Tests/Microsoft.DotNet.ApiCompatibility.Tests/Rules/CannotAddOrRemoveVirtualKeywordTests.cs index 87d9440ed56d..9fe768c6270e 100644 --- a/src/Tests/Microsoft.DotNet.ApiCompatibility.Tests/Rules/CannotAddOrRemoveVirtualKeywordTests.cs +++ b/src/Tests/Microsoft.DotNet.ApiCompatibility.Tests/Rules/CannotAddOrRemoveVirtualKeywordTests.cs @@ -72,6 +72,13 @@ public static IEnumerable RemovedCases() (DifferenceType.Removed, "M:CompatTests.First.remove_F(CompatTests.First.EventHandler)"), (DifferenceType.Removed, "E:CompatTests.First.F")), }; + // effectively sealed containing type + yield return new object[] { + CreateType(" class", "private First() {}", " public virtual void F() {}"), + CreateType(" class", "private First() {}", " public void F() {}"), + false, + CreateDifferences(), + }; } public static IEnumerable AddedCases() diff --git a/src/Tests/Microsoft.DotNet.ApiCompatibility.Tests/Rules/CannotChangeVisibilityTests.cs b/src/Tests/Microsoft.DotNet.ApiCompatibility.Tests/Rules/CannotChangeVisibilityTests.cs new file mode 100644 index 000000000000..3ee2023ee734 --- /dev/null +++ b/src/Tests/Microsoft.DotNet.ApiCompatibility.Tests/Rules/CannotChangeVisibilityTests.cs @@ -0,0 +1,426 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using Microsoft.CodeAnalysis; +using System.Collections.Generic; +using Microsoft.DotNet.ApiCompatibility.Abstractions; +using Microsoft.DotNet.ApiCompatibility.Tests; +using Xunit; + +namespace Microsoft.DotNet.ApiCompatibility.Rules.Tests +{ + public class CannotChangeVisibilityTests + { + private static readonly TestRuleFactory s_ruleFactory = new((settings, context) => new CannotChangeVisibility(settings, context)); + + /* + * Tests for: + * - Reduce visibility of type + * - Expand visibility of type + * - Expand visibility of member + * - Restricting visibility of protected member inside sealed type + * - Restricting visibility of protected member inside type without accessible constructor + * - Restricting visibility of member + * - Strict mode + */ + + public static TheoryData TestCases => new() + { + // Reduce visibility of type + { + @" +namespace CompatTests +{ + public class First {} +} +", + @" +namespace CompatTests +{ + internal class First {} +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotReduceVisibility, string.Empty, DifferenceType.Changed, "T:CompatTests.First") +} + }, + // Reducing visibility of internal type to protected + { + @" +namespace CompatTests +{ + public class First { + internal int F = 0; + } +} +", + @" +namespace CompatTests +{ + public class First { + protected int F = 0; + } +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotReduceVisibility, string.Empty, DifferenceType.Changed, "F:CompatTests.First.F") +} + }, + // Reducing visibility of protected type to internal + { + @" +namespace CompatTests +{ + public class First { + protected int F = 0; + } +} +", + @" +namespace CompatTests +{ + public class First { + internal int F = 0; + } +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotReduceVisibility, string.Empty, DifferenceType.Changed, "F:CompatTests.First.F") +} + }, + // Expand visibility of type + { + @" +namespace CompatTests +{ + internal class First {} +} +", + @" +namespace CompatTests +{ + public class First {} +} +", +new CompatDifference[] {} + }, + // Expand visibility of member + { + @" +namespace CompatTests +{ + public class First { + protected int F; + } +} +", + @" +namespace CompatTests +{ + public class First { + public int F; + } +} +", +new CompatDifference[] {} + }, + // Reducing visibility of protected member inside sealed type. + // Since we don't visit private members, we don't issue a diagnostic here. + // We suppress the warning for declaring protected members in sealed types, + // since we want to check for a different diagnostic. + { + @" +namespace CompatTests +{ + public sealed class First { + +#pragma warning disable CS0628 + protected int F; + } +} +", + @" +namespace CompatTests +{ + public sealed class First { + +#pragma warning disable CS0169 + private int F; + } +} +", +new CompatDifference[] {} + }, + // Reducing visibility of protected member inside type without accessible constructor + // Since we don't visit private members, we don't issue a diagnostic here. + { + @" +namespace CompatTests +{ + public class First { + private First() {} + +#pragma warning disable CS0628 + protected int F; + } +} +", + @" +namespace CompatTests +{ + public class First { + private First() {} + +#pragma warning disable CS0169 + private int F; + } +} +", +new CompatDifference[] {} + }, + // Reduce visibility of member + { + @" +namespace CompatTests +{ + public class First { + public int F; + } +} +", + @" +namespace CompatTests +{ + public class First { + protected int F; + } +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotReduceVisibility, string.Empty, DifferenceType.Changed, "F:CompatTests.First.F") +} + } + }; + + public static TheoryData StrictMode => new() + { + // Reduce visibility of type + { + @" +namespace CompatTests +{ + public class First {} +} +", + @" +namespace CompatTests +{ + internal class First {} +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotReduceVisibility, string.Empty, DifferenceType.Changed, "T:CompatTests.First") +} + }, + // Expand visibility of type + { + @" +namespace CompatTests +{ + internal class First {} +} +", + @" +namespace CompatTests +{ + public class First {} +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotExpandVisibility, string.Empty, DifferenceType.Changed, "T:CompatTests.First") +} + }, + // Expand visibility of member + { + @" +namespace CompatTests +{ + public class First { + protected int F; + } +} +", + @" +namespace CompatTests +{ + public class First { + public int F; + } +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotExpandVisibility, string.Empty, DifferenceType.Changed, "F:CompatTests.First.F") +} + }, + // Reducing visibility of protected member inside sealed type + // Since we don't visit private members, we don't issue a diagnostic here. + // We suppress the warning for declaring protected members in sealed types, + // since we want to check for a different diagnostic. + { + @" +namespace CompatTests +{ + public sealed class First { + +#pragma warning disable CS0628 + protected int F; + } +} +", + @" +namespace CompatTests +{ + public sealed class First { + +#pragma warning disable CS0169 + private int F; + } +} +", +new CompatDifference[] {} + }, + // Reducing visibility of protected member inside type without accessible constructor + // Since we don't visit private members, we don't issue a diagnostic here. + { + @" +namespace CompatTests +{ + public class First { + private First() {} + +#pragma warning disable CS0628 + protected int F; + } +} +", + @" +namespace CompatTests +{ + public class First { + private First() {} + +#pragma warning disable CS0169 + private int F; + } +} +", +new CompatDifference[] {} + }, + // Reduce visibility of member + { + @" +namespace CompatTests +{ + public class First { + public int F; + } +} +", + @" +namespace CompatTests +{ + public class First { + protected int F; + } +} +", +new CompatDifference[] { + CompatDifference.CreateWithDefaultMetadata(DiagnosticIds.CannotReduceVisibility, string.Empty, DifferenceType.Changed, "F:CompatTests.First.F") +} + } + }; + + public static TheoryData NoInternals => new() + { + // No diagnostic on expanding visibility of type from internal to public + { + @" +namespace CompatTests +{ + internal class First {} +} +", + @" +namespace CompatTests +{ + public class First {} +} +", +new CompatDifference[] {} + }, + // No diagnostic on expanding visibility of member from protected to protected internal + { + @" +namespace CompatTests +{ + public class First { + protected int F; + } +} +", + @" +namespace CompatTests +{ + public class First { + protected internal int F; + } +} +", +new CompatDifference[] {} + }, + }; + + [Theory] + [MemberData(nameof(TestCases))] + public void EnsureDiagnosticIsReported(string leftSyntax, string rightSyntax, CompatDifference[] expected) + { + IAssemblySymbol left = SymbolFactory.GetAssemblyFromSyntax(leftSyntax); + IAssemblySymbol right = SymbolFactory.GetAssemblyFromSyntax(rightSyntax); + ApiComparer differ = new(s_ruleFactory, new ApiComparerSettings(includeInternalSymbols: true)); + + IEnumerable actual = differ.GetDifferences(left, right); + + Assert.Equal(expected, actual); + } + + [Theory] + [MemberData(nameof(StrictMode))] + public void EnsureDiagnosticIsReportedInStrictMode(string leftSyntax, string rightSyntax, CompatDifference[] expected) + { + IAssemblySymbol left = SymbolFactory.GetAssemblyFromSyntax(leftSyntax); + IAssemblySymbol right = SymbolFactory.GetAssemblyFromSyntax(rightSyntax); + ApiComparer differ = new(s_ruleFactory, new ApiComparerSettings( + includeInternalSymbols: true, + strictMode: true)); + + IEnumerable actual = differ.GetDifferences(left, right); + + Assert.Equal(expected, actual); + } + + [Theory] + [MemberData(nameof(NoInternals))] + public void EnsureReportedInStrictModeWithoutInternalSymbols(string leftSyntax, string rightSyntax, CompatDifference[] expected) + { + IAssemblySymbol left = SymbolFactory.GetAssemblyFromSyntax(leftSyntax); + IAssemblySymbol right = SymbolFactory.GetAssemblyFromSyntax(rightSyntax); + ApiComparer differ = new(s_ruleFactory, new ApiComparerSettings( + includeInternalSymbols: false, + strictMode: true)); + + IEnumerable actual = differ.GetDifferences(left, right); + + Assert.Equal(expected, actual); + } + } +} diff --git a/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFirstTimeUseNoticeSentinel.cs b/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFirstTimeUseNoticeSentinel.cs index 843f882ffdb6..31cbcfe77881 100644 --- a/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFirstTimeUseNoticeSentinel.cs +++ b/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFirstTimeUseNoticeSentinel.cs @@ -26,7 +26,7 @@ public GivenAFirstTimeUseNoticeSentinel() _fileSystemMockBuilder = FileSystemMockBuilder.Create(); } - [Fact(Skip ="Product.Version not set correctly when running tests")] + [Fact(Skip = "Product.Version not set correctly when running tests")] public void TheSentinelHasTheCurrentVersionInItsName() { FirstTimeUseNoticeSentinel.SENTINEL.Should().Contain($"{Product.Version}"); @@ -174,6 +174,11 @@ public ITemporaryDirectory CreateTemporaryDirectory() throw new NotImplementedException(); } + public string CreateTemporarySubdirectory() + { + throw new NotImplementedException(); + } + public IEnumerable EnumerateDirectories(string path) { throw new NotImplementedException(); diff --git a/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFunctionReturnStringAndFakeFileSystem.cs b/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFunctionReturnStringAndFakeFileSystem.cs index 10e3f1930814..6802977214b2 100644 --- a/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFunctionReturnStringAndFakeFileSystem.cs +++ b/src/Tests/Microsoft.DotNet.Configurer.UnitTests/GivenAFunctionReturnStringAndFakeFileSystem.cs @@ -173,6 +173,11 @@ public IEnumerable EnumerateFileSystemEntries(string path) throw new UnauthorizedAccessException(); } + public string CreateTemporarySubdirectory() + { + throw new NotImplementedException(); + } + public string GetCurrentDirectory() { throw new NotImplementedException(); diff --git a/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageInstallerTests.cs b/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageInstallerTests.cs index aeba497e7ff5..db570d99bc2a 100644 --- a/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageInstallerTests.cs +++ b/src/Tests/Microsoft.DotNet.PackageInstall.Tests/ToolPackageInstallerTests.cs @@ -1059,7 +1059,7 @@ private static string GetTestLocalFeedPath() => private readonly string _testTargetframework = BundledTargetFramework.GetTargetFrameworkMoniker(); private const string TestPackageVersion = "1.0.4"; private static readonly PackageId TestPackageId = new PackageId("global.tool.console.demo"); - private static readonly IEnumerable TestFrameworks = new NuGetFramework[] { NuGetFramework.Parse("netcoreapp2.1")}; + private static readonly IEnumerable TestFrameworks = new NuGetFramework[] { NuGetFramework.Parse("netcoreapp2.1") }; public ToolPackageInstallerTests(ITestOutputHelper log) : base(log) { diff --git a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToTargetEolFrameworks.cs b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToTargetEolFrameworks.cs index 87b9db12a322..bbfcd6df7408 100644 --- a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToTargetEolFrameworks.cs +++ b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToTargetEolFrameworks.cs @@ -23,6 +23,7 @@ public GivenThatWeWantToTargetEolFrameworks(ITestOutputHelper log) : base(log) [InlineData("netcoreapp1.0")] [InlineData("netcoreapp3.0")] [InlineData("netcoreapp2.1")] + [InlineData("net5.0")] public void It_warns_that_framework_is_out_of_support(string targetFrameworks) { var testProject = new TestProject() diff --git a/src/Tests/Microsoft.NET.TestFramework/Mock/FileSystemMockBuilder.cs b/src/Tests/Microsoft.NET.TestFramework/Mock/FileSystemMockBuilder.cs index 7eb9e5381989..0c47207c56f5 100644 --- a/src/Tests/Microsoft.NET.TestFramework/Mock/FileSystemMockBuilder.cs +++ b/src/Tests/Microsoft.NET.TestFramework/Mock/FileSystemMockBuilder.cs @@ -154,7 +154,7 @@ public void CreateDirectory(string path) else { DirectoryNode directoryNode = new DirectoryNode(); - directoryNode = (DirectoryNode) current.Subs.GetOrAdd(p , directoryNode); + directoryNode = (DirectoryNode)current.Subs.GetOrAdd(p, directoryNode); current = directoryNode; } } @@ -237,7 +237,7 @@ public DirectoryNode GetParentOfDirectoryNode(string path) } PathModel pathModel = CreateFullPathModel(path); - if (current.Subs.TryGetValue(pathModel.FileOrDirectoryName(), out var node) ) + if (current.Subs.TryGetValue(pathModel.FileOrDirectoryName(), out var node)) { if (node is FileNode) { @@ -283,7 +283,7 @@ public PathModel(string path) } string[] pathArray = path.Split( - new[] {directorySeparatorChar, altDirectorySeparatorChar}, + new[] { directorySeparatorChar, altDirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); Volume = volume; PathArray = pathArray; @@ -451,7 +451,7 @@ public void Move(string source, string destination) if (_files.TryGetNodeParent(destination, out DirectoryNode current) && current != null) { - sourceFileNode = (FileNode) current.Subs.GetOrAdd(new PathModel(destination).FileOrDirectoryName(), sourceFileNode); + sourceFileNode = (FileNode)current.Subs.GetOrAdd(new PathModel(destination).FileOrDirectoryName(), sourceFileNode); sourceParent.Subs.TryRemove(new PathModel(source).FileOrDirectoryName(), out _); } else @@ -525,9 +525,9 @@ public bool Exists(string path) if (_files.TryGetNodeParent(path, out DirectoryNode current)) { - PathModel pathModel = new PathModel(path); + PathModel pathModel = new PathModel(path); - return current.Subs.TryGetValue(pathModel.FileOrDirectoryName(), out var node) + return current.Subs.TryGetValue(pathModel.FileOrDirectoryName(), out var node) && node is DirectoryNode; } @@ -579,6 +579,11 @@ public void CreateDirectory(string path) _files.CreateDirectory(path); } + public string CreateTemporarySubdirectory() + { + return CreateTemporaryDirectory().DirectoryPath; + } + public void Delete(string path, bool recursive) { if (path == null) throw new ArgumentNullException(nameof(path)); diff --git a/src/Tests/dotnet-new.Tests/CommonTemplatesTests.cs b/src/Tests/dotnet-new.Tests/CommonTemplatesTests.cs index 55b5a6c717f2..6d7e54013ed7 100644 --- a/src/Tests/dotnet-new.Tests/CommonTemplatesTests.cs +++ b/src/Tests/dotnet-new.Tests/CommonTemplatesTests.cs @@ -2,11 +2,14 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. // +using System.Diagnostics; +using System.Runtime.InteropServices; using System.Text.RegularExpressions; using System.Xml.Linq; using Microsoft.DotNet.Cli.Utils; using Microsoft.NET.TestFramework.Assertions; using Microsoft.NET.TestFramework.Commands; +using Microsoft.TemplateEngine.TestHelper; using Xunit.Abstractions; namespace Microsoft.DotNet.Cli.New.IntegrationTests @@ -191,7 +194,7 @@ public void AllCommonItemsCreate(string expectedTemplateName, string templateSho .Should() .ExitWith(0) .And.NotHaveStdErr() - .And.HaveStdOut($@"The template ""{expectedTemplateName}"" was created successfully."); + .And.HaveStdOutContaining($@"The template ""{expectedTemplateName}"" was created successfully."); Directory.Delete(workingDir, true); } @@ -274,6 +277,47 @@ public void GlobalJsonTests(string expectedContent, params string[] parameters) Directory.Delete(workingDir, true); } + [Fact] + public void NuGetConfigPermissions() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + //runs only on Unix + return; + } + + string templateShortName = "nugetconfig"; + string expectedTemplateName = "NuGet Config"; + string workingDir = TestUtils.CreateTemporaryFolder(); + + new DotnetNewCommand(_log, templateShortName) + .WithCustomHive(_fixture.HomeDirectory) + .WithWorkingDirectory(workingDir) + .Execute() + .Should() + .ExitWith(0) + .And.NotHaveStdErr() + .And.HaveStdOutContaining($@"The template ""{expectedTemplateName}"" was created successfully."); + + var process = Process.Start(new ProcessStartInfo() + { + FileName = "/bin/sh", + Arguments = "-c \"ls -la\"", + WorkingDirectory = workingDir + }); + + new Command(process) + .WorkingDirectory(workingDir) + .CaptureStdOut() + .CaptureStdErr() + .Execute() + .Should() + .ExitWith(0) + .And.HaveStdOutMatching("^-rw-------.*nuget.config$", RegexOptions.Multiline); + + Directory.Delete(workingDir, true); + } + #region Project templates language features tests /// diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.cs.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.cs.json index 82bea1ef0409..d1d88c76fe0d 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.cs.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.cs.json @@ -1,6 +1,8 @@ -{ +{ "author": "Microsoft", "name": "Konfigurace NuGet", "description": "Soubor pro konfiguraci umístění, ve kterých bude NuGet hledat balíčky", + "postActions/chmod/description": "Apply permissions", + "postActions/chmod/manualInstructions/default/text": "Run 'chmod 600 nuget.config'", "postActions/open-file/description": "Otevře nuget.config v editoru." } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.de.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.de.json index b983e6d2d1ad..18f356e9c6f5 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.de.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.de.json @@ -1,6 +1,8 @@ -{ +{ "author": "Microsoft", "name": "NuGet-Konfig.", "description": "Eine Datei zum Konfigurieren der Speicherorte, in denen NuGet nach Paketen suchen wird", + "postActions/chmod/description": "Apply permissions", + "postActions/chmod/manualInstructions/default/text": "Run 'chmod 600 nuget.config'", "postActions/open-file/description": "Öffnet „nuget.config“ im Editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.en.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.en.json index 865325dd1c23..143e248642a9 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.en.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.en.json @@ -2,5 +2,7 @@ "author": "Microsoft", "name": "NuGet Config", "description": "A file for configuring the locations NuGet will search for packages", + "postActions/chmod/description": "Apply permissions", + "postActions/chmod/manualInstructions/default/text": "Run 'chmod 600 nuget.config'", "postActions/open-file/description": "Opens nuget.config in the editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.es.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.es.json index 04089d5f4bfa..5f91f7a5af39 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.es.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.es.json @@ -1,6 +1,8 @@ -{ +{ "author": "Microsoft", "name": "Configuración de NuGet", "description": "Un archivo para configurar las ubicaciones en las que NuGet buscará paquetes", + "postActions/chmod/description": "Apply permissions", + "postActions/chmod/manualInstructions/default/text": "Run 'chmod 600 nuget.config'", "postActions/open-file/description": "Abre nuget.config en el editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.fr.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.fr.json index 700e16049331..28a67a159c76 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.fr.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.fr.json @@ -1,6 +1,8 @@ -{ +{ "author": "Microsoft", "name": "Configuration NuGet", "description": "Un fichier permettant de configurer les emplacements où NuGet recherche des packages", + "postActions/chmod/description": "Apply permissions", + "postActions/chmod/manualInstructions/default/text": "Run 'chmod 600 nuget.config'", "postActions/open-file/description": "Ouvre nuget.config dans l’éditeur" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.it.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.it.json index 2f84f93ab81a..873a84caf979 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.it.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.it.json @@ -1,6 +1,8 @@ -{ +{ "author": "Microsoft", "name": "Configurazione NuGet", "description": "Un file per la configurazione del NuGet dei percorsi eseguirà la ricerca dei pacchetti", + "postActions/chmod/description": "Apply permissions", + "postActions/chmod/manualInstructions/default/text": "Run 'chmod 600 nuget.config'", "postActions/open-file/description": "Apre nuget.config nell'editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.ja.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.ja.json index c997c478eac1..7280b14d9d86 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.ja.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.ja.json @@ -1,6 +1,8 @@ -{ +{ "author": "Microsoft", "name": "NuGet Config", "description": "NuGet がパッケージが検索する場所を構成するためのファイル", + "postActions/chmod/description": "Apply permissions", + "postActions/chmod/manualInstructions/default/text": "Run 'chmod 600 nuget.config'", "postActions/open-file/description": "エディターで nuget.config を開く" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.ko.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.ko.json index 747ec7b7905b..8d869ea664b9 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.ko.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.ko.json @@ -1,6 +1,8 @@ -{ +{ "author": "Microsoft", "name": "NuGet 구성", "description": "NuGet 패키지를 검색할 위치를 구성하는 파일", + "postActions/chmod/description": "Apply permissions", + "postActions/chmod/manualInstructions/default/text": "Run 'chmod 600 nuget.config'", "postActions/open-file/description": "편집기에서 nuget.config를 엽니다" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.pl.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.pl.json index 3c2e9b287671..8bfa055d2d17 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.pl.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.pl.json @@ -1,6 +1,8 @@ -{ +{ "author": "Microsoft", "name": "Konfigurowanie NuGet", "description": "Plik na potrzeby konfigurowania lokalizacji NuGet będzie wyszukiwać pakietów", + "postActions/chmod/description": "Apply permissions", + "postActions/chmod/manualInstructions/default/text": "Run 'chmod 600 nuget.config'", "postActions/open-file/description": "Otwiera plik nuget.config w edytorze" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.pt-BR.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.pt-BR.json index 5a2378f253bd..fe564346cd64 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.pt-BR.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.pt-BR.json @@ -1,6 +1,8 @@ -{ +{ "author": "Microsoft", "name": "Configuração do NuGet", "description": "Um arquivo para configurar os locais em que o NuGet procurará pacotes", + "postActions/chmod/description": "Apply permissions", + "postActions/chmod/manualInstructions/default/text": "Run 'chmod 600 nuget.config'", "postActions/open-file/description": "Abre nuget.config no editor" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.ru.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.ru.json index 97c939fbc9c8..7dc44c68b7c2 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.ru.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.ru.json @@ -1,6 +1,8 @@ -{ +{ "author": "Майкрософт", "name": "Конфигурация NuGet", "description": "Файл для настройки расположений, в которых NuGet будет искать пакеты", + "postActions/chmod/description": "Apply permissions", + "postActions/chmod/manualInstructions/default/text": "Run 'chmod 600 nuget.config'", "postActions/open-file/description": "Открывает файл nuget.config в редакторе" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.tr.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.tr.json index 45c054930e2c..581456abee49 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.tr.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.tr.json @@ -1,6 +1,8 @@ -{ +{ "author": "Microsoft", "name": "NuGet Yapılandırması", "description": "NuGet'in paketleri arama yapacağı konumları yapılandırmaya yönelik dosya", + "postActions/chmod/description": "Apply permissions", + "postActions/chmod/manualInstructions/default/text": "Run 'chmod 600 nuget.config'", "postActions/open-file/description": "Düzenleyicide nuget.config’i açar" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.zh-Hans.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.zh-Hans.json index 6508a27358c5..c717eb33eef5 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.zh-Hans.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.zh-Hans.json @@ -1,6 +1,8 @@ -{ +{ "author": "Microsoft", "name": "NuGet 配置", "description": "用于配置位置 NuGet 的文件将搜索包", + "postActions/chmod/description": "Apply permissions", + "postActions/chmod/manualInstructions/default/text": "Run 'chmod 600 nuget.config'", "postActions/open-file/description": "在编辑器中打开 nuget.config" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.zh-Hant.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.zh-Hant.json index 5a58f8891e1c..56c90a06ddb4 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.zh-Hant.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/localize/templatestrings.zh-Hant.json @@ -1,6 +1,8 @@ -{ +{ "author": "Microsoft", "name": "NuGet 組態", "description": "用於設定檔案位置 NuGet 將會搜尋套件", + "postActions/chmod/description": "Apply permissions", + "postActions/chmod/manualInstructions/default/text": "Run 'chmod 600 nuget.config'", "postActions/open-file/description": "在編輯器中開啟 nuget.config" } \ No newline at end of file diff --git a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/template.json b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/template.json index d525790e49b3..72992942035a 100644 --- a/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/template.json +++ b/template_feed/Microsoft.DotNet.Common.ItemTemplates/content/Nuget/.template.config/template.json @@ -27,6 +27,21 @@ } }, "postActions": [ + { + "id": "chmod", + "condition": "(OS != \"Windows_NT\") && (HostIdentifier == \"dotnetcli\" || HostIdentifier == \"dotnetcli-preview\")", + "description": "Apply permissions", + "manualInstructions": [ + { + "text": "Run 'chmod 600 nuget.config'" + } + ], + "actionId": "cb9a6cf3-4f5c-4860-b9d2-03a574959774", + "args": { + "600": "nuget.config" + }, + "continueOnError": false + }, { "id": "open-file", "condition": "(HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")", @@ -39,4 +54,4 @@ "continueOnError": true } ] -} \ No newline at end of file +} diff --git a/testAsset.props b/testAsset.props index 8acb3b6f3d52..96f7069126c5 100644 --- a/testAsset.props +++ b/testAsset.props @@ -14,6 +14,6 @@ false - 2.4.1-pre.build.4059 + 2.4.2