diff --git a/src/Squirrel/ReleasePackage.cs b/src/Squirrel/ReleasePackage.cs
index 643fadfcb..1a611ee1a 100644
--- a/src/Squirrel/ReleasePackage.cs
+++ b/src/Squirrel/ReleasePackage.cs
@@ -160,34 +160,42 @@ public string CreateReleasePackage(string outputFile, string packagesRootDir = n
// We must do this, or PathTooLongException may be thrown for some unicode entry names.
public static void ExtractZipDecoded(string zipFilePath, string outFolder, string directoryFilter = null)
{
- var zf = new ZipFile(zipFilePath);
+ Utility.Retry(() =>
+ {
+ var zf = new ZipFile(zipFilePath);
- foreach (ZipEntry zipEntry in zf) {
- if (!zipEntry.IsFile) continue;
+ foreach(ZipEntry zipEntry in zf)
+ {
+ if(!zipEntry.IsFile)
+ continue;
- var entryFileName = Uri.UnescapeDataString(zipEntry.Name);
+ var entryFileName = Uri.UnescapeDataString(zipEntry.Name);
- var buffer = new byte[4096];
- var zipStream = zf.GetInputStream(zipEntry);
-
- var fullZipToPath = Path.Combine(outFolder, entryFileName);
- var directoryName = Path.GetDirectoryName(fullZipToPath);
- var directoryFilter_ = new NameFilter(directoryFilter);
- if (directoryFilter_.IsMatch(directoryName)) {
- if (directoryName.Length > 0) {
- Directory.CreateDirectory(directoryName);
- }
+ var buffer = new byte[4096];
+ var zipStream = zf.GetInputStream(zipEntry);
- using (FileStream streamWriter = File.Create(fullZipToPath)) {
- StreamUtils.Copy(zipStream, streamWriter, buffer);
+ var fullZipToPath = Path.Combine(outFolder, entryFileName);
+ var directoryName = Path.GetDirectoryName(fullZipToPath);
+ var directoryFilter_ = new NameFilter(directoryFilter);
+ if(directoryFilter_.IsMatch(directoryName))
+ {
+ if(directoryName.Length > 0)
+ {
+ Directory.CreateDirectory(directoryName);
+ }
+
+ using(FileStream streamWriter = File.Create(fullZipToPath))
+ {
+ StreamUtils.Copy(zipStream, streamWriter, buffer);
+ }
}
}
- }
- zf.Close();
+ zf.Close();
+ });
}
// Create zip file with entry names %-encoded, as nupkg file does.
- void createZipEncoded(string zipFilePath, string folder)
+ void createZipEncoded(string zipFilePath, string folder)
{
folder = Path.GetFullPath(folder);
var offset = folder.Length + (folder.EndsWith("\\", StringComparison.OrdinalIgnoreCase) ? 1 : 0);
diff --git a/src/Squirrel/RetryUtility.cs b/src/Squirrel/RetryUtility.cs
new file mode 100644
index 000000000..96a4e081e
--- /dev/null
+++ b/src/Squirrel/RetryUtility.cs
@@ -0,0 +1,66 @@
+// From github.com/sillsdev/libpalaso (MIT x11)
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+
+namespace Squirrel
+{
+ ///
+ /// This utility can be used to wrap any call and retry it in a loop
+ /// until specific exceptions no longer occur.
+ ///
+ /// Number of retry attempts, pause time between attempts, and exception types
+ /// can all be specified or left to the defaults.
+ ///
+ /// This class came about as an attempt to mitigate certain IO issues, so the
+ /// defaults are specified along those lines.
+ ///
+ public static class RetryUtility
+ {
+ // Nothing special about these numbers. Just attempting to balance issues of user experience.
+ public const int kDefaultMaxRetryAttempts = 10;
+ public const int kDefaultRetryDelay = 200;
+ private static readonly ISet kDefaultExceptionTypesToRetry = new HashSet { Type.GetType("System.IO.IOException") };
+
+ public static void Retry(Action action, int maxRetryAttempts = kDefaultMaxRetryAttempts, int retryDelay = kDefaultRetryDelay, ISet exceptionTypesToRetry = null)
+ {
+ Retry