diff --git a/src/NuGet.Clients/NuGet.CommandLine/Commands/PackCommand.cs b/src/NuGet.Clients/NuGet.CommandLine/Commands/PackCommand.cs index c41b128eb01..814c2351d54 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/Commands/PackCommand.cs +++ b/src/NuGet.Clients/NuGet.CommandLine/Commands/PackCommand.cs @@ -103,6 +103,12 @@ public Dictionary Properties [Option(typeof(NuGetCommand), "PackageCommandConfigFile")] public new string ConfigFile { get; set; } + [Option(typeof(NuGetCommand), "PackageCommandDeterministic")] + public bool Deterministic { get; set; } + + [Option(typeof(NuGetCommand), "PackageCommandDeterministicTimestamp")] + public string DeterministicTimestamp { get; set; } + public override void ExecuteCommand() { var packArgs = new PackArgs(); @@ -111,6 +117,8 @@ public override void ExecuteCommand() packArgs.OutputDirectory = OutputDirectory; packArgs.BasePath = BasePath; packArgs.MsBuildDirectory = new Lazy(() => MsBuildUtility.GetMsBuildDirectoryFromMsBuildPath(MSBuildPath, MSBuildVersion, Console).Value.Path); + packArgs.Deterministic = Deterministic; + packArgs.DeterministicTimestamp = DeterministicTimestamp; if (!string.IsNullOrEmpty(PackagesDirectory)) { diff --git a/src/NuGet.Clients/NuGet.CommandLine/Commands/ProjectFactory.cs b/src/NuGet.Clients/NuGet.CommandLine/Commands/ProjectFactory.cs index 542cad333b4..a65b291409c 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/Commands/ProjectFactory.cs +++ b/src/NuGet.Clients/NuGet.CommandLine/Commands/ProjectFactory.cs @@ -40,6 +40,9 @@ public class ProjectFactory : IProjectFactory, CoreV2.NuGet.IPropertyProvider, I private IEnvironmentVariableReader _environmentVariableReader; + private bool _deterministic; + private string _deterministicTimestamp; + // Files we want to always exclude from the resulting package private static readonly HashSet ExcludeFiles = new HashSet(StringComparer.OrdinalIgnoreCase) { NuGetConstants.PackageReferenceFile, @@ -73,6 +76,8 @@ public static IProjectFactory ProjectCreator(PackArgs packArgs, string path) { return new ProjectFactory(packArgs.MsBuildDirectory.Value, path, packArgs.Properties) { + _deterministic = packArgs.Deterministic, + _deterministicTimestamp = packArgs.DeterministicTimestamp, IsTool = packArgs.Tool, LogLevel = packArgs.LogLevel, Logger = packArgs.Logger, @@ -236,7 +241,11 @@ public PackageBuilder CreateBuilder(string basePath, NuGetVersion version, strin } } - builder = new PackageBuilder(false, Logger); + // TODO is deterministic-ness tied in here? + builder = new PackageBuilder(_deterministic, Logger) + { + DeterministicTimestamp = _deterministicTimestamp, + }; try { @@ -705,6 +714,7 @@ private PackageDependency CreateDependencyFromProject(dynamic project, Dictionar projectFactory.ProjectProperties = ProjectProperties; projectFactory.SymbolPackageFormat = SymbolPackageFormat; projectFactory.BuildProject(); + // TODO: does deterministicness matter here? var builder = new PackageBuilder(); projectFactory.ExtractMetadata(builder); diff --git a/src/NuGet.Clients/NuGet.CommandLine/NuGetCommand.Designer.cs b/src/NuGet.Clients/NuGet.CommandLine/NuGetCommand.Designer.cs index 547727d2385..4ed6c5b7159 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/NuGetCommand.Designer.cs +++ b/src/NuGet.Clients/NuGet.CommandLine/NuGetCommand.Designer.cs @@ -992,6 +992,15 @@ internal static string PackageCommandDeterministic { return ResourceManager.GetString("PackageCommandDeterministic", resourceCulture); } } + + /// + /// Looks up a localized string similar to Specify the timestamps this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package.. + /// + internal static string PackageCommandDeterministicTimestamp { + get { + return ResourceManager.GetString("PackageCommandDeterministicTimestamp", resourceCulture); + } + } /// /// Looks up a localized string similar to Specifies one or more wildcard patterns to exclude when creating a package.. diff --git a/src/NuGet.Clients/NuGet.CommandLine/NuGetCommand.resx b/src/NuGet.Clients/NuGet.CommandLine/NuGetCommand.resx index a88851860c8..9a46da0262c 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/NuGetCommand.resx +++ b/src/NuGet.Clients/NuGet.CommandLine/NuGetCommand.resx @@ -844,6 +844,9 @@ nuget trusted-signers Remove -Name TrustedRepo Specify if the command should create a deterministic package. Multiple invocations of the pack command will create the exact same package. + + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + Path to certificate file. diff --git a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.cs.xlf b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.cs.xlf index 92234ea0a8d..4a5e0509431 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.cs.xlf +++ b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.cs.xlf @@ -601,6 +601,11 @@ nuget pack foo.nuspec -Version 2.1.0 Určete, jestli má příkaz vytvořit deterministický balíček. Vícenásobné vyvolání příkazu pack vytvoří úplně stejný balíček. + + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + + Specifies one or more wildcard patterns to exclude when creating a package. Určuje nejméně jeden vzor zástupných znaků, který se má vyloučit při vytváření balíčku. diff --git a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.de.xlf b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.de.xlf index eba8cf99b68..d8cf9df1ac4 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.de.xlf +++ b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.de.xlf @@ -601,6 +601,11 @@ nuget pack foo.nuspec -Version 2.1.0 Geben Sie an, ob der Befehl ein deterministisches Paket erstellen soll. Durch mehrere Aufrufe des Paketbefehls wird genau dasselbe Paket erstellt. + + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + + Specifies one or more wildcard patterns to exclude when creating a package. Gibt mindestens ein Platzhaltermuster an, das beim Erstellen eines Pakets ausgeschlossen werden soll. diff --git a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.es.xlf b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.es.xlf index 8120bfe337d..ea80678ce2d 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.es.xlf +++ b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.es.xlf @@ -601,6 +601,11 @@ nuget pack foo.nuspec -Version 2.1.0 Especifique si el comando debe crear un paquete determinista. Varias invocaciones del comando pack crearán exactamente el mismo paquete. + + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + + Specifies one or more wildcard patterns to exclude when creating a package. Especifica uno o varios patrones de comodines que se excluirán al crear un paquete. diff --git a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.fr.xlf b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.fr.xlf index 1cdf7d3d133..787c3a013cf 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.fr.xlf +++ b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.fr.xlf @@ -601,6 +601,11 @@ nuget pack foo.nuspec -Version 2.1.0 Spécifiez si la commande doit créer un package déterministe. Plusieurs appels de la commande pack créent exactement le même package. + + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + + Specifies one or more wildcard patterns to exclude when creating a package. Spécifie un ou plusieurs modèles à caractère générique à exclure durant la création d'un package. diff --git a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.it.xlf b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.it.xlf index 755221d9500..ae59bcbacc6 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.it.xlf +++ b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.it.xlf @@ -601,6 +601,11 @@ nuget pack foo.nuspec -Version 2.1.0 Specificare se il comando deve creare un pacchetto deterministico. Le chiamate multiple del comando pack creeranno lo stesso pacchetto. + + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + + Specifies one or more wildcard patterns to exclude when creating a package. Specifica uno o più caratteri jolly da escludere durante la creazione di un pacchetto. diff --git a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.ja.xlf b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.ja.xlf index 46774eda212..fc9d8d87a28 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.ja.xlf +++ b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.ja.xlf @@ -601,6 +601,11 @@ nuget pack foo.nuspec -Version 2.1.0 コマンドで決定論的パッケージを作成する必要があるかどうかを指定します。パック コマンドを複数回呼び出すと、まったく同じパッケージが作成されます。 + + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + + Specifies one or more wildcard patterns to exclude when creating a package. パッケージの作成時に除外する 1 つまたは複数のワイルドカード パターンを指定します。 diff --git a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.ko.xlf b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.ko.xlf index 503bb058b06..bacd7309c10 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.ko.xlf +++ b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.ko.xlf @@ -601,6 +601,11 @@ nuget pack foo.nuspec -Version 2.1.0 명령이 결정적 패키지를 생성해야 하는지 여부를 지정합니다. pack 명령을 여러 번 호출하면 정확히 동일한 패키지가 생성됩니다. + + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + + Specifies one or more wildcard patterns to exclude when creating a package. 패키지를 만들 때 제외할 와일드카드 패턴을 하나 이상 지정합니다. diff --git a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.pl.xlf b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.pl.xlf index 755f63c75fb..f34ff304b99 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.pl.xlf +++ b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.pl.xlf @@ -601,6 +601,11 @@ nuget pack foo.nuspec -Version 2.1.0 Określ, czy polecenie powinno utworzyć pakiet deterministyczny. Wiele wywołań polecenia pakietu spowoduje utworzenie dokładnie tego samego pakietu. + + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + + Specifies one or more wildcard patterns to exclude when creating a package. Określa co najmniej jeden wzorzec symboli wieloznacznych do wykluczenia podczas tworzenia pakietu. diff --git a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.pt-BR.xlf b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.pt-BR.xlf index 157c49bc5f9..2f2fe2e3da3 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.pt-BR.xlf +++ b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.pt-BR.xlf @@ -601,6 +601,11 @@ nuget pack foo.nuspec -Version 2.1.0 Especifique se o comando deve criar um pacote determinístico. Várias invocações do comando pack criarão exatamente o mesmo pacote. + + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + + Specifies one or more wildcard patterns to exclude when creating a package. Especifica um ou mais padrões de curinga a serem excluídos ao criar um pacote. diff --git a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.ru.xlf b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.ru.xlf index c302303a4a4..7d996829782 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.ru.xlf +++ b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.ru.xlf @@ -601,6 +601,11 @@ nuget pack foo.nuspec -Version 2.1.0 Укажите, должна ли эта команда создавать детерминированный пакет. Если вызвать команду упаковки несколько раз, будет создаваться одинаковый пакет. + + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + + Specifies one or more wildcard patterns to exclude when creating a package. Задает шаблоны подстановочных знаков, которые нужно исключить при создании пакета. diff --git a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.tr.xlf b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.tr.xlf index 6d15495393a..3fa896e4be6 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.tr.xlf +++ b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.tr.xlf @@ -601,6 +601,11 @@ nuget pack foo.nuspec -Sürüm 2.1.0 Komutun belirleyici bir paket oluşturması gerekip gerekmediğini belirtin. Paket komutunun birden çok çağrısı aynı paketi oluşturur. + + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + + Specifies one or more wildcard patterns to exclude when creating a package. Bir paket oluşturulurken hariç tutulacak bir veya daha fazla joker karakter desenini belirtir. diff --git a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.zh-Hans.xlf b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.zh-Hans.xlf index 0522ecf5b29..95589f87cdd 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.zh-Hans.xlf +++ b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.zh-Hans.xlf @@ -601,6 +601,11 @@ nuget pack foo.nuspec -Version 2.1.0 指定命令是否应创建确定性包。多次调用包命令将创建完全相同的包。 + + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + + Specifies one or more wildcard patterns to exclude when creating a package. 创建包时指定一个或多个要排除的通配符模式。 diff --git a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.zh-Hant.xlf b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.zh-Hant.xlf index 9e2f11cfd1c..658dde98b72 100644 --- a/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.zh-Hant.xlf +++ b/src/NuGet.Clients/NuGet.CommandLine/xlf/NuGetCommand.zh-Hant.xlf @@ -601,6 +601,11 @@ nuget pack foo.nuspec -Version 2.1.0 指定命令是否應該建立確定性封裝。多次呼叫封裝命令將會建立完全相同的封裝。 + + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + Specify the timestamp this command should use when creating a deterministic package. Multiple invocations of the pack command will create the exact same package. + + Specifies one or more wildcard patterns to exclude when creating a package. 指定一或多個在建立套件時要排除的萬用字元模式。 diff --git a/src/NuGet.Core/NuGet.Build.Tasks.Pack/IPackTaskRequest.cs b/src/NuGet.Core/NuGet.Build.Tasks.Pack/IPackTaskRequest.cs index 6c4b4488711..0e79f0a40d6 100644 --- a/src/NuGet.Core/NuGet.Build.Tasks.Pack/IPackTaskRequest.cs +++ b/src/NuGet.Core/NuGet.Build.Tasks.Pack/IPackTaskRequest.cs @@ -76,6 +76,7 @@ public interface IPackTaskRequest string PackageLicenseFile { get; } string Readme { get; } bool Deterministic { get; } + string DeterministicTimestamp { get; } string PackageIcon { get; } } } diff --git a/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTask.cs b/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTask.cs index fc28b03d1ff..b18935acb2a 100644 --- a/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTask.cs +++ b/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTask.cs @@ -92,6 +92,7 @@ internal PackTask(IEnvironmentVariableReader environmentVariableReader) public string PackageLicenseExpressionVersion { get; set; } public string Readme { get; set; } public bool Deterministic { get; set; } + public string DeterministicTimestamp { get; set; } public string PackageIcon { get; set; } public ILogger Logger => new MSBuildLogger(Log); @@ -225,6 +226,7 @@ private IPackTaskRequest GetRequest() PackageLicenseExpressionVersion = MSBuildStringUtility.TrimAndGetNullForEmpty(PackageLicenseExpressionVersion), Readme = MSBuildStringUtility.TrimAndGetNullForEmpty(Readme), Deterministic = Deterministic, + DeterministicTimestamp = MSBuildStringUtility.TrimAndGetNullForEmpty(DeterministicTimestamp), PackageIcon = MSBuildStringUtility.TrimAndGetNullForEmpty(PackageIcon), }; } diff --git a/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskLogic.cs b/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskLogic.cs index feee7a8a888..57b86a0eb0c 100644 --- a/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskLogic.cs +++ b/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskLogic.cs @@ -42,6 +42,8 @@ public PackArgs GetPackArgs(IPackTaskRequest request) BasePath = request.NuspecBasePath, NoPackageAnalysis = request.NoPackageAnalysis, NoDefaultExcludes = request.NoDefaultExcludes, + Deterministic = request.Deterministic, + DeterministicTimestamp = request.DeterministicTimestamp, WarningProperties = WarningProperties.GetWarningProperties(request.TreatWarningsAsErrors, request.WarningsAsErrors, request.NoWarn, request.WarningsNotAsErrors), PackTargetArgs = new MSBuildPackTargetArgs() }; @@ -130,7 +132,8 @@ public PackageBuilder GetPackageBuilder(IPackTaskRequest request) ReleaseNotes = request.ReleaseNotes, RequireLicenseAcceptance = request.RequireLicenseAcceptance, EmitRequireLicenseAcceptance = request.RequireLicenseAcceptance, - PackageTypes = ParsePackageTypes(request) + PackageTypes = ParsePackageTypes(request), + DeterministicTimestamp = request.DeterministicTimestamp, }; if (request.DevelopmentDependency) diff --git a/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskRequest.cs b/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskRequest.cs index 8c4630a213f..a418b666ef6 100644 --- a/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskRequest.cs +++ b/src/NuGet.Core/NuGet.Build.Tasks.Pack/PackTaskRequest.cs @@ -71,6 +71,7 @@ public class PackTaskRequest : IPackTaskRequest public string PackageLicenseExpression { get; set; } public string PackageLicenseExpressionVersion { get; set; } public bool Deterministic { get; set; } + public string DeterministicTimestamp { get; set; } public string PackageIcon { get; set; } } } diff --git a/src/NuGet.Core/NuGet.Build.Tasks/NuGet.Build.Tasks.Pack.targets b/src/NuGet.Core/NuGet.Build.Tasks/NuGet.Build.Tasks.Pack.targets index cd86627edcb..d1809cc59e0 100644 --- a/src/NuGet.Core/NuGet.Build.Tasks/NuGet.Build.Tasks.Pack.targets +++ b/src/NuGet.Core/NuGet.Build.Tasks/NuGet.Build.Tasks.Pack.targets @@ -218,6 +218,10 @@ Copyright (c) .NET Foundation. All rights reserved. + + $(SOURCE_DATE_EPOCH) + + diff --git a/src/NuGet.Core/NuGet.Commands/CommandArgs/PackArgs.cs b/src/NuGet.Core/NuGet.Commands/CommandArgs/PackArgs.cs index a1caf32fd2b..08121dcdfc8 100644 --- a/src/NuGet.Core/NuGet.Commands/CommandArgs/PackArgs.cs +++ b/src/NuGet.Core/NuGet.Commands/CommandArgs/PackArgs.cs @@ -45,6 +45,7 @@ public class PackArgs public bool Tool { get; set; } public string Version { get; set; } public bool Deterministic { get; set; } + public string DeterministicTimestamp { get; set; } public WarningProperties WarningProperties { get; set; } public MSBuildPackTargetArgs PackTargetArgs { get; set; } public Dictionary Properties diff --git a/src/NuGet.Core/NuGet.Commands/CommandRunners/PackCommandRunner.cs b/src/NuGet.Core/NuGet.Commands/CommandRunners/PackCommandRunner.cs index 6c56f87946f..8cb61368119 100644 --- a/src/NuGet.Core/NuGet.Commands/CommandRunners/PackCommandRunner.cs +++ b/src/NuGet.Core/NuGet.Commands/CommandRunners/PackCommandRunner.cs @@ -454,7 +454,10 @@ private PackageBuilder CreatePackageBuilderFromNuspec(string path) !_packArgs.ExcludeEmptyDirectories, _packArgs.Deterministic, _packArgs.Logger, - _packArgs.Version); + _packArgs.Version) + { + DeterministicTimestamp = _packArgs.DeterministicTimestamp, + }; } return new PackageBuilder( @@ -464,7 +467,10 @@ private PackageBuilder CreatePackageBuilderFromNuspec(string path) !_packArgs.ExcludeEmptyDirectories, _packArgs.Deterministic, _packArgs.Logger, - _packArgs.Version); + _packArgs.Version) + { + DeterministicTimestamp = _packArgs.DeterministicTimestamp, + }; } private bool BuildFromProjectFile(string path) diff --git a/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Shipped.txt b/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Shipped.txt index cb654564a4e..98ec928b0aa 100644 --- a/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Shipped.txt +++ b/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Shipped.txt @@ -341,6 +341,8 @@ NuGet.Commands.PackArgs.Build.set -> void ~NuGet.Commands.PackArgs.CurrentDirectory.set -> void NuGet.Commands.PackArgs.Deterministic.get -> bool NuGet.Commands.PackArgs.Deterministic.set -> void +~NuGet.Commands.PackArgs.DeterministicTimestamp.get -> string +~NuGet.Commands.PackArgs.DeterministicTimestamp.set -> void ~NuGet.Commands.PackArgs.Exclude.get -> System.Collections.Generic.IEnumerable ~NuGet.Commands.PackArgs.Exclude.set -> void NuGet.Commands.PackArgs.ExcludeEmptyDirectories.get -> bool diff --git a/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Shipped.txt b/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Shipped.txt index 0241081ed25..28b932396b1 100644 --- a/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Shipped.txt +++ b/src/NuGet.Core/NuGet.Commands/PublicAPI/net8.0/PublicAPI.Shipped.txt @@ -341,6 +341,8 @@ NuGet.Commands.PackArgs.Build.set -> void ~NuGet.Commands.PackArgs.CurrentDirectory.set -> void NuGet.Commands.PackArgs.Deterministic.get -> bool NuGet.Commands.PackArgs.Deterministic.set -> void +~NuGet.Commands.PackArgs.DeterministicTimestamp.get -> string +~NuGet.Commands.PackArgs.DeterministicTimestamp.set -> void ~NuGet.Commands.PackArgs.Exclude.get -> System.Collections.Generic.IEnumerable ~NuGet.Commands.PackArgs.Exclude.set -> void NuGet.Commands.PackArgs.ExcludeEmptyDirectories.get -> bool diff --git a/src/NuGet.Core/NuGet.Packaging/GlobalSuppressions.cs b/src/NuGet.Core/NuGet.Packaging/GlobalSuppressions.cs index 781c426ab35..6e8e70f51f8 100644 --- a/src/NuGet.Core/NuGet.Packaging/GlobalSuppressions.cs +++ b/src/NuGet.Core/NuGet.Packaging/GlobalSuppressions.cs @@ -44,7 +44,6 @@ [assembly: SuppressMessage("Build", "CA1062:In externally visible method 'IEnumerable PackageArchiveReader.CopyFiles(string destination, IEnumerable packageFiles, ExtractPackageFileDelegate extractFile, ILogger logger, CancellationToken token)', validate parameter 'packageFiles' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGet.Packaging.PackageArchiveReader.CopyFiles(System.String,System.Collections.Generic.IEnumerable{System.String},NuGet.Packaging.Core.ExtractPackageFileDelegate,NuGet.Common.ILogger,System.Threading.CancellationToken)~System.Collections.Generic.IEnumerable{System.String}")] [assembly: SuppressMessage("Build", "CA1062:In externally visible method 'IEnumerable PackageArchiveReader.EnumeratePackageEntries(IEnumerable packageFiles, string packageDirectory)', validate parameter 'packageFiles' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGet.Packaging.PackageArchiveReader.EnumeratePackageEntries(System.Collections.Generic.IEnumerable{System.String},System.String)~System.Collections.Generic.IEnumerable{NuGet.Packaging.ZipFilePair}")] [assembly: SuppressMessage("Build", "CA1062:In externally visible method 'Stream PackageArchiveReader.GetStream(string path)', validate parameter 'path' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGet.Packaging.PackageArchiveReader.GetStream(System.String)~System.IO.Stream")] -[assembly: SuppressMessage("Build", "CA1801:Parameter deterministic of method .ctor is never used. Remove the parameter or use it in the method body.", Justification = "", Scope = "member", Target = "~M:NuGet.Packaging.PackageBuilder.#ctor(System.Boolean,System.Boolean)")] [assembly: SuppressMessage("Build", "CA1062:In externally visible method 'void PackageBuilder.AddFiles(string basePath, string source, string destination, string exclude = null)', validate parameter 'source' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGet.Packaging.PackageBuilder.AddFiles(System.String,System.String,System.String,System.String)")] [assembly: SuppressMessage("Build", "CA1822:Member GenerateRelationshipId does not access instance data and can be marked as static (Shared in VisualBasic)", Justification = "", Scope = "member", Target = "~M:NuGet.Packaging.PackageBuilder.GenerateRelationshipId(System.String)~System.String")] [assembly: SuppressMessage("Build", "CA1062:In externally visible method 'void PackageBuilder.Populate(ManifestMetadata manifestMetadata)', validate parameter 'manifestMetadata' is non-null before using it. If appropriate, throw an ArgumentNullException when the argument is null or add a Code Contract precondition asserting non-null argument.", Justification = "", Scope = "member", Target = "~M:NuGet.Packaging.PackageBuilder.Populate(NuGet.Packaging.ManifestMetadata)")] diff --git a/src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/PackageBuilder.cs b/src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/PackageBuilder.cs index 3d828ae8f41..f27e5df0bc6 100644 --- a/src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/PackageBuilder.cs +++ b/src/NuGet.Core/NuGet.Packaging/PackageCreation/Authoring/PackageBuilder.cs @@ -32,6 +32,7 @@ public class PackageBuilder : IPackageMetadata internal const string ManifestRelationType = "manifest"; private readonly bool _includeEmptyDirectories; private readonly bool _deterministic; + private readonly DateTimeOffset _deterministicTimestamp = DateTimeOffset.UtcNow; private readonly ILogger _logger; private readonly string? _versionOverride; @@ -69,6 +70,7 @@ public PackageBuilder(string path, string? basePath, Func? prope : this(path, basePath, propertyProvider, includeEmptyDirectories, deterministic, logger, versionOverride: "") { } + public PackageBuilder(string path, string? basePath, Func? propertyProvider, bool includeEmptyDirectories, bool deterministic, ILogger logger, string versionOverride) : this(path, basePath, propertyProvider, includeEmptyDirectories, deterministic, versionOverride) { @@ -78,8 +80,8 @@ public PackageBuilder(string path, string? basePath, Func? prope public PackageBuilder(string path, string? basePath, Func? propertyProvider, bool includeEmptyDirectories, bool deterministic) : this(path, basePath, propertyProvider, includeEmptyDirectories, deterministic, versionOverride: "") { - } + public PackageBuilder(string path, string? basePath, Func? propertyProvider, bool includeEmptyDirectories, bool deterministic, string versionOverride) : this(includeEmptyDirectories, deterministic) { @@ -118,7 +120,6 @@ public PackageBuilder(Stream stream, string? basePath, Func? pro public PackageBuilder(bool deterministic) : this(includeEmptyDirectories: false, deterministic: deterministic) { - } public PackageBuilder() @@ -127,19 +128,19 @@ public PackageBuilder() } public PackageBuilder(bool deterministic, ILogger logger) - : this(includeEmptyDirectories: false, deterministic: deterministic, logger) + : this(includeEmptyDirectories: false, deterministic: deterministic, logger: logger) { } private PackageBuilder(bool includeEmptyDirectories, bool deterministic) - : this(includeEmptyDirectories: false, deterministic: deterministic, logger: NullLogger.Instance) + : this(includeEmptyDirectories: includeEmptyDirectories, deterministic: deterministic, logger: NullLogger.Instance) { } private PackageBuilder(bool includeEmptyDirectories, bool deterministic, ILogger logger) { _includeEmptyDirectories = includeEmptyDirectories; - _deterministic = false; // fix in https://github.com/NuGet/Home/issues/8601 + _deterministic = deterministic; _logger = logger; Files = new Collection(); DependencyGroups = new Collection(); @@ -156,6 +157,77 @@ private PackageBuilder(bool includeEmptyDirectories, bool deterministic, ILogger Properties = new Dictionary(StringComparer.OrdinalIgnoreCase); } + public string DeterministicTimestamp + { + init + { + if (value == null || value == "true") + { + _deterministicTimestamp = DateTimeOffset.UtcNow; + } + else if (value == "false") + { + _deterministic = false; + _deterministicTimestamp = DateTimeOffset.UtcNow; + } + else if (TryParseTimestamp(value, out DateTimeOffset parsedDateTimestamp)) + { + _deterministicTimestamp = parsedDateTimestamp; + } + else + { + string errorMessage = $"Invalid timestamp value {value}"; + throw new PackagingException(NuGetLogCode.NU5030, errorMessage); + } + Console.WriteLine($"XXXX PackageBuilder.DeterministicTimestamp_set: {_deterministicTimestamp}"); + Console.Error.WriteLine($"XXXX PackageBuilder.DeterministicTimestamp_set: {_deterministicTimestamp}"); + } + } + + private static bool TryParseTimestamp(string timestamp, out DateTimeOffset result) + { + if (long.TryParse(timestamp, NumberStyles.None, CultureInfo.InvariantCulture, out long unixTimeSeconds)) + { + result = DateTimeOffset.FromUnixTimeSeconds(unixTimeSeconds); + return true; + } + + var parsedDate = ParseRfc3339(timestamp); + if (parsedDate is DateTimeOffset nonNullParsedTimestamp) + { + result = nonNullParsedTimestamp; + return true; + } + + result = default; + return false; + } + + private static DateTimeOffset? ParseRfc3339(string input) + { + // RFC3339 patterns: + // 1. Full date-time with 'Z' (UTC) + // 2. Full date-time with offset (+HH:mm or -HH:mm) + // 3. Full date-time with fractional seconds and 'Z' + // 4. Full date-time with fractional seconds and offset + string[] formats = { + "yyyy-MM-dd'T'HH:mm:ss'Z'", + "yyyy-MM-dd'T'HH:mm:sszzz", + "yyyy-MM-dd'T'HH:mm:ss.FFFK", // K handles Z or offset + "yyyy-MM-dd'T'HH:mm:ss.fffffffK" + }; + if (DateTimeOffset.TryParseExact(input, formats, + CultureInfo.InvariantCulture, + DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, + out DateTimeOffset result)) + { + return result; + } + + return null; + } + + // Set to empty to enforce a stricter nullability contract. // This will be validated in the Save() method before writing the manifest public string Id { get; set; } = string.Empty; @@ -853,7 +925,9 @@ private ZipArchiveEntry CreateEntry(ZipArchive package, string entryName, Compre var entry = package.CreateEntry(entryName, compressionLevel); if (_deterministic) { - entry.LastWriteTime = ZipFormatMinDate; + Console.WriteLine($"XXXX PackageBuilder.CreateEntry: {_deterministicTimestamp}"); + Console.Error.WriteLine($"YYYY PackageBuilder.CreateEntry: {_deterministicTimestamp}"); + entry.LastWriteTime = _deterministicTimestamp; } return entry; } @@ -909,7 +983,7 @@ private SortedSet WriteFiles(ZipArchive package, SortedSet files package, file.Path, stream, - lastWriteTime: _deterministic ? ZipFormatMinDate : file.LastWriteTime, + lastWriteTime: _deterministic ? _deterministicTimestamp : file.LastWriteTime, warningMessage); var fileExtension = Path.GetExtension(file.Path); diff --git a/src/NuGet.Core/NuGet.Packaging/PublicAPI/net472/PublicAPI.Shipped.txt b/src/NuGet.Core/NuGet.Packaging/PublicAPI/net472/PublicAPI.Shipped.txt index 08ade694f5a..772a30dfb85 100644 --- a/src/NuGet.Core/NuGet.Packaging/PublicAPI/net472/PublicAPI.Shipped.txt +++ b/src/NuGet.Core/NuGet.Packaging/PublicAPI/net472/PublicAPI.Shipped.txt @@ -550,6 +550,7 @@ NuGet.Packaging.PackageBuilder.Copyright.set -> void NuGet.Packaging.PackageBuilder.DependencyGroups.get -> System.Collections.ObjectModel.Collection! NuGet.Packaging.PackageBuilder.Description.get -> string? NuGet.Packaging.PackageBuilder.Description.set -> void +NuGet.Packaging.PackageBuilder.DeterministicTimestamp.init -> void NuGet.Packaging.PackageBuilder.DevelopmentDependency.get -> bool NuGet.Packaging.PackageBuilder.DevelopmentDependency.set -> void NuGet.Packaging.PackageBuilder.EmitRequireLicenseAcceptance.get -> bool diff --git a/src/NuGet.Core/NuGet.Packaging/PublicAPI/net8.0/PublicAPI.Shipped.txt b/src/NuGet.Core/NuGet.Packaging/PublicAPI/net8.0/PublicAPI.Shipped.txt index dc4271e9591..0c287064f99 100644 --- a/src/NuGet.Core/NuGet.Packaging/PublicAPI/net8.0/PublicAPI.Shipped.txt +++ b/src/NuGet.Core/NuGet.Packaging/PublicAPI/net8.0/PublicAPI.Shipped.txt @@ -550,6 +550,7 @@ NuGet.Packaging.PackageBuilder.Copyright.set -> void NuGet.Packaging.PackageBuilder.DependencyGroups.get -> System.Collections.ObjectModel.Collection! NuGet.Packaging.PackageBuilder.Description.get -> string? NuGet.Packaging.PackageBuilder.Description.set -> void +NuGet.Packaging.PackageBuilder.DeterministicTimestamp.init -> void NuGet.Packaging.PackageBuilder.DevelopmentDependency.get -> bool NuGet.Packaging.PackageBuilder.DevelopmentDependency.set -> void NuGet.Packaging.PackageBuilder.EmitRequireLicenseAcceptance.get -> bool diff --git a/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NuGetPackCommandTest.cs b/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NuGetPackCommandTest.cs index 4a933faa8f1..c58c04802dc 100644 --- a/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NuGetPackCommandTest.cs +++ b/test/NuGet.Clients.Tests/NuGet.CommandLine.Test/NuGetPackCommandTest.cs @@ -6296,7 +6296,7 @@ private static void VerifyNuspecRoundTrips(PackageArchiveReader nupkgReader, str } } - [Fact(Skip = "https://github.com/NuGet/Home/issues/8601")] + [Fact] public void PackCommand_Deterministic_MultiplePackInvocations_CreateIdenticalPackages() { var nugetexe = Util.GetNuGetExePath(); @@ -6329,7 +6329,11 @@ public void PackCommand_Deterministic_MultiplePackInvocations_CreateIdenticalPac "); - var command = "pack packageA.nuspec -Deterministic -OutputDirectory {0}"; + var timestamp = new DateTimeOffset(year: 2020, month: 1, day: 1, + hour: 0, minute: 0, second: 0, + offset: TimeSpan.Zero); + + var command = "pack packageA.nuspec -Deterministic -DeterministicTimestamp {0} -OutputDirectory {1}"; byte[][] packageBytes = new byte[2][]; for (var i = 0; i < 2; i++) @@ -6340,7 +6344,7 @@ public void PackCommand_Deterministic_MultiplePackInvocations_CreateIdenticalPac var r = CommandRunner.Run( nugetexe, workingDirectory, - string.Format(command, path), + string.Format(command, timestamp.ToString("o"), path), testOutputHelper: _testOutputHelper); Assert.True(0 == r.ExitCode, r.Output + " " + r.Errors); diff --git a/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/PackCommandTests.cs b/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/PackCommandTests.cs index 2fde4a2bb9b..7909fda258f 100644 --- a/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/PackCommandTests.cs +++ b/test/NuGet.Core.FuncTests/Dotnet.Integration.Test/PackCommandTests.cs @@ -4951,9 +4951,13 @@ public void PackCommand_WithGeneratePackageOnBuildSet_CanPublish() } } - [PlatformFact(Platform.Windows, Skip = "https://github.com/NuGet/Home/issues/8601")] + [PlatformFact(Platform.Windows)] public void PackCommand_Deterministic_MultiplePackInvocations_CreateIdenticalPackages() { + var deterministicTimestamp = new DateTimeOffset(year: 2020, month: 1, day: 1, + hour: 0, minute: 0, second: 0, + offset: TimeSpan.Zero); + using (var testDirectory = _dotnetFixture.CreateTestDirectory()) { var projectName = "ClassLibrary1"; @@ -4965,6 +4969,7 @@ public void PackCommand_Deterministic_MultiplePackInvocations_CreateIdenticalPac { var xml = XDocument.Load(stream); ProjectFileUtils.AddProperty(xml, "Deterministic", "true"); + ProjectFileUtils.AddProperty(xml, "DeterministicTimestamp", deterministicTimestamp.ToString("o")); ProjectFileUtils.WriteXmlToFile(xml, stream); }