Catch both old uppercase 5-char hash and newer lowercase 5-char hash when migrating to new hash (#1552)

Co-authored-by: Oleksii Holub <1935960+Tyrrrz@users.noreply.github.com>
This commit is contained in:
Nick 2026-06-20 20:16:48 +09:00 committed by GitHub
parent 97485c280b
commit 8bc9fe7c72
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -38,17 +38,21 @@ internal partial class ExportAssetDownloader(string workingDirPath, bool reuse)
return _previousPathsByUrl[url] = filePath; return _previousPathsByUrl[url] = filePath;
// Check for a file cached by the legacy naming scheme (5-char hash) and rename it // Check for a file cached by the legacy naming scheme (5-char hash) and rename it
// to the new naming scheme to preserve backwards compatibility with existing exports // to the new naming scheme to preserve backwards compatibility with existing exports.
// This will catch both the 5-char lowercase hash and the 5-char uppercase hash variants.
if (reuse) if (reuse)
{ {
var legacyFilePath = Path.Combine(workingDirPath, GetLegacyFileNameFromUrl(url)); var legacyFileNames = GetLegacyFileNamesFromUrl(url);
foreach (var legacyFileName in legacyFileNames)
{
var legacyFilePath = Path.Combine(workingDirPath, legacyFileName);
if (File.Exists(legacyFilePath)) if (File.Exists(legacyFilePath))
{ {
// Overwrite in case the destination file was created concurrently between our // Overwrite in case the destination file was created concurrently between our
// earlier existence check and this move operation // earlier existence check and this move operation
try try
{ {
File.Move(legacyFilePath, filePath, overwrite: true); File.Move(legacyFilePath, filePath, true);
return _previousPathsByUrl[url] = filePath; return _previousPathsByUrl[url] = filePath;
} }
catch (IOException) catch (IOException)
@ -58,6 +62,7 @@ internal partial class ExportAssetDownloader(string workingDirPath, bool reuse)
} }
} }
} }
}
Directory.CreateDirectory(workingDirPath); Directory.CreateDirectory(workingDirPath);
@ -137,13 +142,19 @@ internal partial class ExportAssetDownloader
); );
// Legacy naming used a 5-char hash, kept for backwards compatibility with existing exports // Legacy naming used a 5-char hash, kept for backwards compatibility with existing exports
private static string GetLegacyFileNameFromUrl(string url) => private static IReadOnlyList<string> GetLegacyFileNamesFromUrl(string url)
{
var hashData = SHA256.HashData(Encoding.UTF8.GetBytes(NormalizeUrl(url)));
return
[
// Lowercase variant (introduced in 2.46.1)
GetFileNameFromUrl(url, Convert.ToHexStringLower(hashData).Truncate(5)),
// Uppercase variant (original)
GetFileNameFromUrl( GetFileNameFromUrl(
url, url,
SHA256 Convert.ToHexString(hashData).Truncate(5)
.HashData(Encoding.UTF8.GetBytes(NormalizeUrl(url))) )
.Pipe(Convert.ToHexStringLower) ];
// 5 chars = 20 bits, reaches 1% collision probability at ~145 files }
.Truncate(5)
);
} }