Skip to content

Commit 97715ca

Browse files
authored
UniversalPackages: Add incrementality to avoid re-downloading packages (#639)
1 parent 0f07122 commit 97715ca

File tree

1 file changed

+38
-4
lines changed

1 file changed

+38
-4
lines changed

src/UniversalPackages/DownloadUniversalPackages.cs

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,25 @@ public override bool Execute()
141141
return true;
142142
}
143143

144+
// To ensure package integrity, eg to deal with cancellation, we will download the packages to a temp dir and then move (directory moves are atomic) them to the final location.
145+
var remappedPackages = new List<UniversalPackage>(packages.Count);
146+
var packagePathMappings = new List<(string TempPath, string FinalPath)>(packages.Count);
147+
foreach (UniversalPackage package in packages)
148+
{
149+
string remappedPath = package.Path.TrimEnd(Path.DirectorySeparatorChar) + ".tmp_download";
150+
if (Directory.Exists(remappedPath))
151+
{
152+
Directory.Delete(remappedPath, recursive: true);
153+
}
154+
155+
var remappedPackage = new UniversalPackage(package.Project, package.Feed, package.PackageName, package.PackageVersion, remappedPath, package.Filter);
156+
remappedPackages.Add(remappedPackage);
157+
packagePathMappings.Add((remappedPath, package.Path));
158+
}
159+
144160
// Batch downloads are more efficient, so generate the required json file.
145161
string packageListJsonPath = Path.GetFullPath(PackageListJsonPath);
146-
CreatePackageListJson(packages, packageListJsonPath);
162+
CreatePackageListJson(remappedPackages, packageListJsonPath);
147163

148164
string? patVar = GetPatVar();
149165
if (string.IsNullOrWhiteSpace(patVar))
@@ -163,6 +179,12 @@ public override bool Execute()
163179
return false;
164180
}
165181

182+
// Move the downloaded packages to their final locations.
183+
foreach ((string tempPath, string finalPath) in packagePathMappings)
184+
{
185+
Directory.Move(tempPath, finalPath);
186+
}
187+
166188
return !Log.HasLoggedErrors;
167189
}
168190

@@ -253,10 +275,11 @@ [new ProjectGraphEntryPoint(ProjectFile, globalProperties)],
253275
}
254276
}
255277

256-
// Validate Paths are either unique so downloads don't stomp on each other. ArtifactTool doesn't do this for us, but probably should, especially since it downloads them in parallel.
278+
var packagesToDownload = new List<UniversalPackage>(packages.Count);
257279
var downloadPaths = new Dictionary<string, UniversalPackage>(PathHelper.PathComparer);
258280
foreach (UniversalPackage package in packages)
259281
{
282+
// Validate Paths are either unique so downloads don't stomp on each other. ArtifactTool doesn't do this for us, but probably should, especially since it downloads them in parallel.
260283
if (downloadPaths.TryGetValue(package.Path, out UniversalPackage? existingPackage))
261284
{
262285
Log.LogError($"Found multiple universal package download requests to the same path: {package.Path}. Packages '{existingPackage.PackageName} {existingPackage.PackageVersion}' and '{package.PackageName}.{package.PackageVersion}'");
@@ -265,12 +288,23 @@ [new ProjectGraphEntryPoint(ProjectFile, globalProperties)],
265288
{
266289
downloadPaths.Add(package.Path, package);
267290
}
291+
292+
// Filter out packages which are already downloaded. This is an optimization for incremental restores.
293+
if (Directory.Exists(package.Path))
294+
{
295+
Log.LogMessage($"Skipping '{package.PackageName}.{package.PackageVersion}' as '{package.Path}' already exists.");
296+
}
297+
else
298+
{
299+
Log.LogMessage($"Need to download '{package.PackageName}.{package.PackageVersion}' to '{package.Path}'.");
300+
packagesToDownload.Add(package);
301+
}
268302
}
269303

270-
return packages;
304+
return packagesToDownload;
271305
}
272306

273-
private void CreatePackageListJson(IReadOnlyCollection<UniversalPackage> packages, string packageListJsonPath)
307+
private void CreatePackageListJson(List<UniversalPackage> packages, string packageListJsonPath)
274308
{
275309
var batchRequest = new UniversalPackageBatchDownloadRequest(packages);
276310
var options = new JsonSerializerOptions()

0 commit comments

Comments
 (0)