diff --git a/.gitignore b/.gitignore
index 986d57b..1b11d69 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,8 @@
### VisualStudio ###
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.suo
@@ -273,9 +275,6 @@ paket-files/
# CodeRush
.cr/
-# WinMerge
-*.bak
-
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
@@ -297,4 +296,8 @@ tools/**
# By default, sensitive information, such as encrypted password
# should be stored in the .pubxml.user file.
-# End of https://www.gitignore.io/api/visualstudio
+
+# WinMerge
+*.bak
+
+# End of https://www.gitignore.io/api/visualstudio
\ No newline at end of file
diff --git a/GitReleaseManager.yaml b/GitReleaseManager.yaml
index 592d292..725bcfa 100644
--- a/GitReleaseManager.yaml
+++ b/GitReleaseManager.yaml
@@ -1,7 +1,7 @@
create:
include-footer: false
footer-heading: Where to get it
- footer-content: You can download this release from [nuget.org](https://www.nuget.org/packages/Picton/{milestone})
+ footer-content: You can download this release from [nuget.org](https://www.nuget.org/packages/Picton.Messaging/{milestone})
footer-includes-milestone: true
milestone-replace-text: '{milestone}'
export:
diff --git a/Source/Picton.Messaging.IntegrationTests/Picton.Messaging.IntegrationTests.csproj b/Source/Picton.Messaging.IntegrationTests/Picton.Messaging.IntegrationTests.csproj
index ea95e11..e26b001 100644
--- a/Source/Picton.Messaging.IntegrationTests/Picton.Messaging.IntegrationTests.csproj
+++ b/Source/Picton.Messaging.IntegrationTests/Picton.Messaging.IntegrationTests.csproj
@@ -8,7 +8,7 @@
-
+
diff --git a/Source/Picton.Messaging.IntegrationTests/Program.cs b/Source/Picton.Messaging.IntegrationTests/Program.cs
index f732e25..2fbf1cb 100644
--- a/Source/Picton.Messaging.IntegrationTests/Program.cs
+++ b/Source/Picton.Messaging.IntegrationTests/Program.cs
@@ -110,7 +110,7 @@ public static void ProcessSimpleMessages(string queueName, CloudStorageAccount s
Stopwatch sw = null;
// Configure the message pump
- var messagePump = new AsyncMessagePump(queueName, storageAccount, 10, TimeSpan.FromMinutes(1), 3, metrics)
+ var messagePump = new AsyncMessagePump(queueName, storageAccount, 10, null, TimeSpan.FromMinutes(1), 3, metrics)
{
OnMessage = (message, cancellationToken) =>
{
@@ -157,7 +157,7 @@ public static void ProcessMessagesWithHandlers(string queueName, CloudStorageAcc
Stopwatch sw = null;
// Configure the message pump
- var messagePump = new AsyncMessagePumpWithHandlers(queueName, storageAccount, 10, TimeSpan.FromMinutes(1), 3, metrics);
+ var messagePump = new AsyncMessagePumpWithHandlers(queueName, storageAccount, 10, null, TimeSpan.FromMinutes(1), 3, metrics);
messagePump.OnQueueEmpty = cancellationToken =>
{
// Stop the timer
diff --git a/Source/Picton.Messaging.UnitTests/AsyncMessagePumpTests.cs b/Source/Picton.Messaging.UnitTests/AsyncMessagePumpTests.cs
index e86ba15..668c837 100644
--- a/Source/Picton.Messaging.UnitTests/AsyncMessagePumpTests.cs
+++ b/Source/Picton.Messaging.UnitTests/AsyncMessagePumpTests.cs
@@ -5,6 +5,7 @@
using Moq;
using Shouldly;
using System;
+using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
@@ -24,7 +25,7 @@ public void Null_cloudQueue_throws()
{
Should.Throw(() =>
{
- var messagePump = new AsyncMessagePump("myqueue", (CloudStorageAccount)null, 1, TimeSpan.FromMinutes(1), 3);
+ var messagePump = new AsyncMessagePump("myqueue", (CloudStorageAccount)null, 1, null, TimeSpan.FromMinutes(1), 3);
});
}
@@ -34,7 +35,7 @@ public void Number_of_concurrent_tasks_too_small_throws()
Should.Throw(() =>
{
var mockStorageAccount = GetMockStorageAccount(null, null);
- var messagePump = new AsyncMessagePump("myqueue", mockStorageAccount.Object, 0, TimeSpan.FromMinutes(1), 3);
+ var messagePump = new AsyncMessagePump("myqueue", mockStorageAccount.Object, 0, null, TimeSpan.FromMinutes(1), 3);
});
}
@@ -44,7 +45,7 @@ public void DequeueCount_too_small_throws()
Should.Throw(() =>
{
var mockStorageAccount = GetMockStorageAccount(null, null);
- var messagePump = new AsyncMessagePump("myqueue", mockStorageAccount.Object, 1, TimeSpan.FromMinutes(1), 0);
+ var messagePump = new AsyncMessagePump("myqueue", mockStorageAccount.Object, 1, null, TimeSpan.FromMinutes(1), 0);
});
}
@@ -59,7 +60,7 @@ public void Start_without_OnMessage_throws()
var mockBlobClient = GetMockBlobClient(mockBlobContainer);
var mockStorageAccount = GetMockStorageAccount(mockBlobClient, mockQueueClient);
- var messagePump = new AsyncMessagePump("myqueue", mockStorageAccount.Object, 1, TimeSpan.FromMinutes(1), 3);
+ var messagePump = new AsyncMessagePump(queueName, mockStorageAccount.Object, 1, null, TimeSpan.FromMinutes(1), 3);
// Act
Should.Throw(() => messagePump.Start());
@@ -76,7 +77,7 @@ public void Stopping_without_starting()
var mockBlobClient = GetMockBlobClient(mockBlobContainer);
var mockStorageAccount = GetMockStorageAccount(mockBlobClient, mockQueueClient);
- var messagePump = new AsyncMessagePump("myqueue", mockStorageAccount.Object, 1, TimeSpan.FromMinutes(1), 3);
+ var messagePump = new AsyncMessagePump(queueName, mockStorageAccount.Object, 1, null, TimeSpan.FromMinutes(1), 3);
// Act
messagePump.Stop();
@@ -102,7 +103,7 @@ public void No_message_processed_when_queue_is_empty()
mockQueue.Setup(q => q.GetMessagesAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Enumerable.Empty());
- var messagePump = new AsyncMessagePump("myqueue", mockStorageAccount.Object, 1, TimeSpan.FromMinutes(1), 3);
+ var messagePump = new AsyncMessagePump(queueName, mockStorageAccount.Object, 1, null, TimeSpan.FromMinutes(1), 3);
messagePump.OnMessage = (message, cancellationToken) =>
{
Interlocked.Increment(ref onMessageInvokeCount);
@@ -180,7 +181,7 @@ public void Message_processed()
return Task.FromResult(true);
});
- var messagePump = new AsyncMessagePump("myqueue", mockStorageAccount.Object, 1, TimeSpan.FromMinutes(1), 3);
+ var messagePump = new AsyncMessagePump(queueName, mockStorageAccount.Object, 1, null, TimeSpan.FromMinutes(1), 3);
messagePump.OnMessage = (message, cancellationToken) =>
{
Interlocked.Increment(ref onMessageInvokeCount);
@@ -260,7 +261,7 @@ public void Poison_message_is_rejected()
return Task.FromResult(true);
});
- var messagePump = new AsyncMessagePump("myqueue", mockStorageAccount.Object, 1, TimeSpan.FromMinutes(1), retries);
+ var messagePump = new AsyncMessagePump(queueName, mockStorageAccount.Object, 1, null, TimeSpan.FromMinutes(1), retries);
messagePump.OnMessage = (message, cancellationToken) =>
{
Interlocked.Increment(ref onMessageInvokeCount);
@@ -289,13 +290,105 @@ public void Poison_message_is_rejected()
// Assert
onMessageInvokeCount.ShouldBe(1);
- onQueueEmptyInvokeCount.ShouldBeGreaterThan(0);
+ onQueueEmptyInvokeCount.ShouldBe(1);
onErrorInvokeCount.ShouldBe(1);
isRejected.ShouldBeTrue();
mockQueue.Verify(q => q.GetMessagesAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.AtLeast(2));
mockQueue.Verify(q => q.DeleteMessageAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(1));
}
+ [Fact]
+ public void Poison_message_is_moved()
+ {
+ // Arrange
+ var onMessageInvokeCount = 0;
+ var onQueueEmptyInvokeCount = 0;
+ var onErrorInvokeCount = 0;
+
+ var isRejected = false;
+ var retries = 3;
+ var lockObject = new Object();
+ var cloudMessage = new CloudQueueMessage("Message");
+
+ var queueName = "myqueue";
+ var poisonQueueName = $"{queueName}-poison";
+
+ var mockQueue = GetMockQueue(queueName);
+ var mockPoisonQueue = GetMockQueue(poisonQueueName);
+ var mockQueueClient = GetMockQueueClient(new[] { mockQueue, mockPoisonQueue });
+ var mockBlobContainer = GetMockBlobContainer();
+ var mockBlobClient = GetMockBlobClient(mockBlobContainer);
+ var mockStorageAccount = GetMockStorageAccount(mockBlobClient, mockQueueClient);
+
+ mockQueue.Setup(q => q.GetMessagesAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync((int messageCount, TimeSpan? visibilityTimeout, QueueRequestOptions options, OperationContext operationContext, CancellationToken cancellationToken) =>
+ {
+ if (cloudMessage != null)
+ {
+ lock (lockObject)
+ {
+ if (cloudMessage != null)
+ {
+ // DequeueCount is a private property. Therefore we must use reflection to change its value
+ var dequeueCountProperty = cloudMessage.GetType().GetProperty("DequeueCount");
+ dequeueCountProperty.SetValue(cloudMessage, retries + 1); // intentionally set 'DequeueCount' to a value exceeding maxRetries to simulate a poison message
+
+ return new[] { cloudMessage };
+ }
+ }
+ }
+ return Enumerable.Empty();
+ });
+ mockQueue.Setup(q => q.DeleteMessageAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns((string messageId, string popReceipt, QueueRequestOptions options, OperationContext operationContext, CancellationToken cancellationToken) =>
+ {
+ lock (lockObject)
+ {
+ cloudMessage = null;
+ }
+ return Task.FromResult(true);
+ });
+ mockPoisonQueue.Setup(q => q.AddMessageAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns((CloudQueueMessage message, TimeSpan? timeToLive, TimeSpan? visibilityTimeout, QueueRequestOptions options, OperationContext operationContext, CancellationToken cancellationToken) =>
+ {
+ // Nothing to do. We just want to ensure this method is invoked.
+ return Task.FromResult(true);
+ });
+
+ var messagePump = new AsyncMessagePump(queueName, mockStorageAccount.Object, 1, poisonQueueName, TimeSpan.FromMinutes(1), retries);
+ messagePump.OnMessage = (message, cancellationToken) =>
+ {
+ Interlocked.Increment(ref onMessageInvokeCount);
+ throw new Exception("An error occured when attempting to process the message");
+ };
+ messagePump.OnQueueEmpty = cancellationToken =>
+ {
+ Interlocked.Increment(ref onQueueEmptyInvokeCount);
+ messagePump.Stop();
+ };
+ messagePump.OnError = (message, exception, isPoison) =>
+ {
+ Interlocked.Increment(ref onErrorInvokeCount);
+ if (isPoison)
+ {
+ lock (lockObject)
+ {
+ isRejected = true;
+ cloudMessage = null;
+ }
+ }
+ };
+
+ // Act
+ messagePump.Start();
+
+ // Assert
+ onMessageInvokeCount.ShouldBe(1);
+ onQueueEmptyInvokeCount.ShouldBe(1);
+ onErrorInvokeCount.ShouldBe(1);
+ isRejected.ShouldBeTrue();
+ mockQueue.Verify(q => q.GetMessagesAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.AtLeast(2));
+ mockQueue.Verify(q => q.DeleteMessageAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(1));
+ mockPoisonQueue.Verify(q => q.AddMessageAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Exactly(1));
+ }
+
[Fact]
public void Exceptions_in_OnQueueEmpty_are_ignored()
{
@@ -316,7 +409,7 @@ public void Exceptions_in_OnQueueEmpty_are_ignored()
mockQueue.Setup(q => q.GetMessagesAsync(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).ReturnsAsync(Enumerable.Empty());
- var messagePump = new AsyncMessagePump("myqueue", mockStorageAccount.Object, 1, TimeSpan.FromMinutes(1), 3);
+ var messagePump = new AsyncMessagePump(queueName, mockStorageAccount.Object, 1, null, TimeSpan.FromMinutes(1), 3);
messagePump.OnMessage = (message, cancellationToken) =>
{
Interlocked.Increment(ref onMessageInvokeCount);
@@ -387,14 +480,23 @@ private static Mock GetMockQueue(string queueName)
}
private static Mock GetMockQueueClient(Mock mockQueue)
+ {
+ return GetMockQueueClient(new[] { mockQueue });
+ }
+
+ private static Mock GetMockQueueClient(IEnumerable> mockQueues)
{
var mockQueueStorageUri = new Uri(QUEUE_STORAGE_URL);
var storageCredentials = GetStorageCredentials();
var mockQueueClient = new Mock(MockBehavior.Strict, mockQueueStorageUri, storageCredentials);
- mockQueueClient
- .Setup(c => c.GetQueueReference(mockQueue.Object.Name))
- .Returns(mockQueue.Object)
- .Verifiable();
+ foreach (var mockQueue in mockQueues.ToArray())
+ {
+ mockQueueClient
+ .Setup(c => c.GetQueueReference(mockQueue.Object.Name))
+ .Returns(mockQueue.Object)
+ .Verifiable();
+
+ }
return mockQueueClient;
}
@@ -428,6 +530,5 @@ private static StorageCredentials GetStorageCredentials()
var storageCredentials = new StorageCredentials("account_name", accountAccessKey);
return storageCredentials;
}
-
}
}
diff --git a/Source/Picton.Messaging.UnitTests/Picton.Messaging.UnitTests.csproj b/Source/Picton.Messaging.UnitTests/Picton.Messaging.UnitTests.csproj
index c66990e..ab34c04 100644
--- a/Source/Picton.Messaging.UnitTests/Picton.Messaging.UnitTests.csproj
+++ b/Source/Picton.Messaging.UnitTests/Picton.Messaging.UnitTests.csproj
@@ -7,8 +7,8 @@
-
-
+
+
diff --git a/Source/Picton.Messaging/AsyncMessagePump.cs b/Source/Picton.Messaging/AsyncMessagePump.cs
index 71fb3b1..42e5f3b 100644
--- a/Source/Picton.Messaging/AsyncMessagePump.cs
+++ b/Source/Picton.Messaging/AsyncMessagePump.cs
@@ -24,6 +24,7 @@ public class AsyncMessagePump
private static readonly ILog _logger = LogProvider.GetLogger(typeof(AsyncMessagePump));
private readonly IQueueManager _queueManager;
+ private readonly IQueueManager _poisonQueueManager;
private readonly int _concurrentTasks;
private readonly TimeSpan? _visibilityTimeout;
private readonly int _maxDequeueCount;
@@ -77,10 +78,11 @@ public class AsyncMessagePump
/// Name of the queue.
/// The cloud storage account.
/// The number of concurrent tasks.
+ /// Name of the queue where messages are automatically moved to when they fail to be processed after 'maxDequeueCount' attempts. You can indicate that you do not want messages to be automatically moved by leaving this value empty. In such a scenario, you are responsible for handling so called 'poinson' messages.
/// The visibility timeout.
/// The maximum dequeue count.
/// The system where metrics are published
- public AsyncMessagePump(string queueName, CloudStorageAccount storageAccount, int concurrentTasks = 25, TimeSpan? visibilityTimeout = null, int maxDequeueCount = 3, IMetrics metrics = null)
+ public AsyncMessagePump(string queueName, CloudStorageAccount storageAccount, int concurrentTasks = 25, string poisonQueueName = null, TimeSpan? visibilityTimeout = null, int maxDequeueCount = 3, IMetrics metrics = null)
{
if (concurrentTasks < 1) throw new ArgumentException("Number of concurrent tasks must be greather than zero", nameof(concurrentTasks));
if (maxDequeueCount < 1) throw new ArgumentException("Number of retries must be greather than zero", nameof(maxDequeueCount));
@@ -90,6 +92,11 @@ public AsyncMessagePump(string queueName, CloudStorageAccount storageAccount, in
_visibilityTimeout = visibilityTimeout;
_maxDequeueCount = maxDequeueCount;
+ if (!string.IsNullOrEmpty(poisonQueueName))
+ {
+ _poisonQueueManager = new QueueManager(poisonQueueName, storageAccount);
+ }
+
if (metrics == null)
{
var noop = new MetricsBuilder();
@@ -275,7 +282,8 @@ public void Stop()
OnError?.Invoke(message, ex, isPoison);
if (isPoison)
{
- // PLEASE NOTE: we use "CancellationToken.None" to ensure a processed message is deleted from the queue even when the message pump is shutting down
+ // PLEASE NOTE: we use "CancellationToken.None" to ensure a processed message is deleted from the queue and moved to poison queue even when the message pump is shutting down
+ if (_poisonQueueManager != null) await _poisonQueueManager.AddMessageAsync(message.Content, null, null, null, null, CancellationToken.None).ConfigureAwait(false);
await _queueManager.DeleteMessageAsync(message, null, null, CancellationToken.None).ConfigureAwait(false);
}
}
diff --git a/Source/Picton.Messaging/AsyncMessagePumpWithHandlers.cs b/Source/Picton.Messaging/AsyncMessagePumpWithHandlers.cs
index b0aad3f..f2bd8d4 100644
--- a/Source/Picton.Messaging/AsyncMessagePumpWithHandlers.cs
+++ b/Source/Picton.Messaging/AsyncMessagePumpWithHandlers.cs
@@ -68,12 +68,13 @@ public Action OnQueueEmpty
/// Name of the queue.
/// The cloud storage account.
/// The number of concurrent tasks.
+ /// Name of the queue where messages are automatically moved to when they fail to be processed after 'maxDequeueCount' attempts. You can indicate that you do not want messages to be automatically moved by leaving this value empty. In such a scenario, you are responsible for handling so called 'poinson' messages.
/// The visibility timeout.
/// The maximum dequeue count.
/// The system where metrics are published
- public AsyncMessagePumpWithHandlers(string queueName, CloudStorageAccount storageAccount, int concurrentTasks = 25, TimeSpan? visibilityTimeout = null, int maxDequeueCount = 3, IMetrics metrics = null)
+ public AsyncMessagePumpWithHandlers(string queueName, CloudStorageAccount storageAccount, int concurrentTasks = 25, string poisonQueueName = null, TimeSpan? visibilityTimeout = null, int maxDequeueCount = 3, IMetrics metrics = null)
{
- _messagePump = new AsyncMessagePump(queueName, storageAccount, concurrentTasks, visibilityTimeout, maxDequeueCount, metrics)
+ _messagePump = new AsyncMessagePump(queueName, storageAccount, concurrentTasks, poisonQueueName, visibilityTimeout, maxDequeueCount, metrics)
{
OnMessage = (message, cancellationToken) =>
{
diff --git a/Source/Picton.Messaging/Picton.Messaging.csproj b/Source/Picton.Messaging/Picton.Messaging.csproj
index c476958..cfae4f1 100644
--- a/Source/Picton.Messaging/Picton.Messaging.csproj
+++ b/Source/Picton.Messaging/Picton.Messaging.csproj
@@ -28,8 +28,8 @@
-
-
+
+
All
@@ -41,11 +41,6 @@
-
-
-
-
-
$(DefineConstants);NETFULL;LIBLOG_PORTABLE
@@ -59,7 +54,7 @@
- $(SolutionDir)\Picton.Messaging.ruleset
+ $(SolutionDir)\StyleCopRules.ruleset
diff --git a/Source/Picton.Messaging.ruleset b/Source/StyleCopRules.ruleset
similarity index 95%
rename from Source/Picton.Messaging.ruleset
rename to Source/StyleCopRules.ruleset
index ccf2352..29c2d50 100644
--- a/Source/Picton.Messaging.ruleset
+++ b/Source/StyleCopRules.ruleset
@@ -1,5 +1,5 @@
-
-
+
+
diff --git a/build.cake b/build.cake
index 187de06..4730c45 100644
--- a/build.cake
+++ b/build.cake
@@ -1,11 +1,11 @@
// Install addins.
-#addin "nuget:?package=Cake.Coveralls&version=0.7.0"
+#addin "nuget:?package=Cake.Coveralls&version=0.8.0"
// Install tools.
#tool "nuget:?package=GitVersion.CommandLine&version=4.0.0-beta0012"
-#tool "nuget:?package=GitReleaseManager&version=0.6.0"
+#tool "nuget:?package=GitReleaseManager&version=0.7.0"
#tool "nuget:?package=OpenCover&version=4.6.519"
-#tool "nuget:?package=ReportGenerator&version=3.1.1"
+#tool "nuget:?package=ReportGenerator&version=3.1.2"
#tool "nuget:?package=coveralls.io&version=1.4.2"
#tool "nuget:?package=xunit.runner.console&version=2.3.1"
@@ -25,7 +25,7 @@ var configuration = Argument("configuration", "Release");
var libraryName = "Picton.Messaging";
var gitHubRepo = "Picton.Messaging";
-var testCoverageFilter = "+[Picton.Messaging]* -[Picton.Messaging]Picton.Messaging.Properties.* -[Picton.Messaging]Picton.Messaging.Logging.*";
+var testCoverageFilter = "+[Picton.Messaging]* -[Picton.Messaging]Picton.Messaging.Properties.* -[Picton.Messaging]Picton.Messaging.Models.* -[Picton.Messaging]Picton.Messaging.Logging.*";
var testCoverageExcludeByAttribute = "*.ExcludeFromCodeCoverage*";
var testCoverageExcludeByFile = "*/*Designer.cs;*/*AssemblyInfo.cs";
@@ -150,8 +150,6 @@ Task("Restore-NuGet-Packages")
{
Sources = new [] {
"https://www.myget.org/F/xunit/api/v3/index.json",
- "https://dotnet.myget.org/F/dotnet-core/api/v3/index.json",
- "https://dotnet.myget.org/F/cli-deps/api/v3/index.json",
"https://api.nuget.org/v3/index.json",
}
});
@@ -161,7 +159,7 @@ Task("Build")
.IsDependentOn("Restore-NuGet-Packages")
.Does(() =>
{
- DotNetCoreBuild(sourceFolder + libraryName + ".sln", new DotNetCoreBuildSettings
+ DotNetCoreBuild($"{sourceFolder}{libraryName}.sln", new DotNetCoreBuildSettings
{
Configuration = configuration,
NoRestore = true,
@@ -233,6 +231,7 @@ Task("Create-NuGet-Package")
IncludeSource = false,
IncludeSymbols = false,
NoBuild = true,
+ NoDependencies = true,
OutputDirectory = outputDir,
ArgumentCustomization = (args) =>
{
@@ -244,7 +243,7 @@ Task("Create-NuGet-Package")
}
};
- DotNetCorePack(sourceFolder + libraryName + "/" + libraryName + ".csproj", settings);
+ DotNetCorePack($"{sourceFolder}{libraryName}/{libraryName}.csproj", settings);
});
Task("Upload-AppVeyor-Artifacts")
diff --git a/build.ps1 b/build.ps1
index ec762b2..0c93a46 100644
--- a/build.ps1
+++ b/build.ps1
@@ -115,11 +115,10 @@ if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
# Make sure that packages.config exist.
if (!(Test-Path $PACKAGES_CONFIG)) {
- Write-Verbose -Message "Downloading packages.config..."
- try {
+ Write-Verbose -Message "Downloading packages.config..."
+ try {
$wc = GetProxyEnabledWebClient
- $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG)
- } catch {
+ $wc.DownloadFile("https://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch {
Throw "Could not download packages.config."
}
}
diff --git a/tools/packages.config b/tools/packages.config
index 31f2b2a..04a5fab 100644
--- a/tools/packages.config
+++ b/tools/packages.config
@@ -1,4 +1,4 @@
-
+