Skip to content

Ensure stable order of files in the nuget package#6649

Closed
omajid wants to merge 1 commit intoNuGet:devfrom
omajid:nuget-archive-deterministic-file-order
Closed

Ensure stable order of files in the nuget package#6649
omajid wants to merge 1 commit intoNuGet:devfrom
omajid:nuget-archive-deterministic-file-order

Conversation

@omajid
Copy link
Copy Markdown
Contributor

@omajid omajid commented Jul 25, 2025

Bug

Fixes: NuGet/Home#14448

Description

We can add files to the nuget package in any order. To provide more reproducibility/deterministicness, add the files in the archive in a well-defined order.

PR Checklist

  • Meaningful title, helpful description and a linked NuGet/Home issue
  • Added tests
  • Link to an issue or pull request to update docs if this PR changes settings, environment variables, new feature, etc.

@dotnet-policy-service dotnet-policy-service Bot added the Community PRs created by someone not in the NuGet team label Jul 25, 2025
@omajid omajid force-pushed the nuget-archive-deterministic-file-order branch from 805fcef to 219c7e8 Compare July 25, 2025 02:26

// Add files that might not come from expanding files on disk
foreach (IPackageFile file in new HashSet<IPackageFile>(Files))
foreach (IPackageFile file in new SortedSet<IPackageFile>(Files, Comparer<IPackageFile>.Create((a, b) => String.CompareOrdinal(a.Path, b.Path))))
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Anyone know if this provides the same sorting order across platforms? The unit tests have many comments like this:

// Linux sorts the first two in different order than Window

And I would love to make sure this ordering is the same no matter the platform.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think it's a culture/locale issue, not an OS issue. I found 3 instances of the comment, and all 3 were doing stringList.OrderBy(s => s), so it didn't specify a string comparison method, and therefore defaults to culture specific sort.

Using this C# program:

string[] values = [ "[Content_Types].xml", "_rel/.rels" ];

List<string> contentTypesFirst = new List<string>();
List<string> relsFirst = new List<string>();

foreach (var culture in System.Globalization.CultureInfo.GetCultures(System.Globalization.CultureTypes.SpecificCultures))
{
    System.Threading.Thread.CurrentThread.CurrentCulture = culture;
    System.Threading.Thread.CurrentThread.CurrentUICulture = culture;
    if (values.OrderBy(v => v).First() == "[Content_Types].xml")
    {
        contentTypesFirst.Add(culture.Name);
    }
    else
    {
        relsFirst.Add(culture.Name);
    }
}

Console.WriteLine("Cultures where [Content_Types].xml comes first: " + string.Join(", ", contentTypesFirst));
Console.WriteLine("Cultures where _rels/.rels comes first: " + string.Join(", ", relsFirst));

Running on both Windows and Ubuntu (on WSL2), there's only 2 CultureInfo's that sort content types first: th-TH and en-US-POSIX

My guess is that the people at the time who wrote that comment probably had a linux machine using the C/POSIX locale and didn't know better.

I didn't test this PR's change, but since it's explicitly using String.CompareOrdinal, I expect it to work. I think new SortedSet<IPackageFile>(Files, StringComparer.Ordinal) is a shorter way to write the equivalent outcome.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for digging into this. C/POSIX makes a lot of sense.

I can't get new SortedSet<IPackageFile>(Files, StringComparer.Ordinal) to work. I think that tries to compare the the IPackageFile objects directly instead of using the nested Path (a string) property:

error CS1503: Argument 2: cannot convert from 'System.StringComparer' to 'System.Collections.Generic.IComparer<IPackageFile>?'

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oops, I missed that it was comparing a property, rather than the object itself. Yes, the anonymous function is needed.

@dotnet-policy-service dotnet-policy-service Bot added Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed and removed Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed labels Aug 1, 2025
@omajid omajid force-pushed the nuget-archive-deterministic-file-order branch from 219c7e8 to 5cbac32 Compare August 7, 2025 22:16
@omajid omajid marked this pull request as ready for review August 7, 2025 22:16
@omajid omajid requested a review from a team as a code owner August 7, 2025 22:16
@omajid omajid requested review from Nigusu-Allehu and zivkan August 7, 2025 22:16
We can add files to the nuget package in any order. To provide more
reproducibility/deterministicness, add the files in the archive in a
well-defined order.

Fixes: NuGet/Home#14448
@omajid omajid force-pushed the nuget-archive-deterministic-file-order branch from 5cbac32 to 3c1ff3f Compare August 8, 2025 21:30
@dotnet-policy-service dotnet-policy-service Bot added the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Aug 15, 2025
@omajid
Copy link
Copy Markdown
Contributor Author

omajid commented Aug 25, 2025

I was away on PTO. I plan to pick this up again in a few days.

@dotnet-policy-service dotnet-policy-service Bot removed the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Aug 25, 2025
@zivkan
Copy link
Copy Markdown
Member

zivkan commented Aug 26, 2025

/azp run

@azure-pipelines
Copy link
Copy Markdown

Azure Pipelines successfully started running 1 pipeline(s).

Copy link
Copy Markdown
Member

@zivkan zivkan left a comment

Choose a reason for hiding this comment

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

There are two tests that need to be updated, they appear to expect files in a specific order. Otherwise, it looks good to me.


// Add files that might not come from expanding files on disk
foreach (IPackageFile file in new HashSet<IPackageFile>(Files))
foreach (IPackageFile file in new SortedSet<IPackageFile>(Files, Comparer<IPackageFile>.Create((a, b) => String.CompareOrdinal(a.Path, b.Path))))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Oops, I missed that it was comparing a property, rather than the object itself. Yes, the anonymous function is needed.

@aortiz-msft
Copy link
Copy Markdown
Collaborator

@omajid - We'd need this PR merged this month for it to make it in .NET 10.

@omajid
Copy link
Copy Markdown
Contributor Author

omajid commented Sep 2, 2025

Thanks for the ping. I will re-prioritize this.

@dotnet-policy-service dotnet-policy-service Bot added the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Sep 10, 2025
@Frulfump
Copy link
Copy Markdown

Oh no this was abandoned, has the ship sailed on .NET 10 now?

@dotnet-policy-service dotnet-policy-service Bot removed the Status:No recent activity PRs that have not had any recent activity and will be closed if the label is not removed label Oct 12, 2025
@nkolev92
Copy link
Copy Markdown
Member

yeah, it's too late now.

Strictly stabilization.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Community PRs created by someone not in the NuGet team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Use a deterministic order for adding files to nuget packages

5 participants