From a5ce4159d838abdd6c6972a62e6e83a0238cf2ef Mon Sep 17 00:00:00 2001 From: Scooletz Date: Mon, 19 May 2014 11:27:11 +0200 Subject: [PATCH 01/21] owner_id @ namespace sometimes can be null int? instead of int used --- NGitLab/NGitLab/Models/Namespace.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NGitLab/NGitLab/Models/Namespace.cs b/NGitLab/NGitLab/Models/Namespace.cs index adea2057..121d57f3 100644 --- a/NGitLab/NGitLab/Models/Namespace.cs +++ b/NGitLab/NGitLab/Models/Namespace.cs @@ -27,6 +27,6 @@ public class Namespace public DateTime UpdatedAt; [DataMember(Name="owner_id")] - public int OwnerId; + public int? OwnerId; } -} \ No newline at end of file +} From 428ce9298b4f613ba2b44e94fc062eb8ba979122 Mon Sep 17 00:00:00 2001 From: Scooletz Date: Tue, 20 May 2014 00:36:56 +0200 Subject: [PATCH 02/21] nuspec updated --- NGitLab.nuspec | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/NGitLab.nuspec b/NGitLab.nuspec index 7992006c..11dfa150 100644 --- a/NGitLab.nuspec +++ b/NGitLab.nuspec @@ -2,17 +2,21 @@ NGitLab - 1.0.0.0 + NGitLab + 1.0.1.0 Scooletz Scooletz Provides a .NET REST client implementation of GitLab API. Provides a .NET REST client implementation of GitLab API with no external dependencies. Uses the private token authentication https://github.com/Scooletz/NGitLab - https://github.com/Scooletz/NGitLab/LICENSE + https://github.com/Scooletz/NGitLab/blob/master/LICENSE GitLab API NGitlab en-US * 1.0.0.0 - initial, majority of methods work, tests based on the locally run VM + + + \ No newline at end of file From 1ac3e918439954e0f94454759dbd1c5f0326454b Mon Sep 17 00:00:00 2001 From: Michel Courtine Date: Mon, 16 Jun 2014 13:12:55 +0200 Subject: [PATCH 03/21] set ToolsVersion to 4.0 in NGitLab.csproj & NGitLab.Tests.csproj to support Xamarin --- NGitLab/NGitLab.Tests/NGitLab.Tests.csproj | 6 ++++-- NGitLab/NGitLab/NGitLab.csproj | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj index 14553c9f..baa1cb7c 100644 --- a/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj @@ -1,5 +1,5 @@  - + Debug @@ -11,6 +11,8 @@ NGitLab.Tests v4.5 512 + 8.0.30703 + 2.0 true @@ -68,4 +70,4 @@ --> - \ No newline at end of file + diff --git a/NGitLab/NGitLab/NGitLab.csproj b/NGitLab/NGitLab/NGitLab.csproj index d2f695e0..b78b430e 100644 --- a/NGitLab/NGitLab/NGitLab.csproj +++ b/NGitLab/NGitLab/NGitLab.csproj @@ -1,5 +1,5 @@  - + Debug @@ -11,6 +11,8 @@ NGitLab v4.5 512 + 8.0.30703 + 2.0 true @@ -92,4 +94,4 @@ --> - \ No newline at end of file + From fabdf6761280bbaaa655330f3adfc7bb4379e741 Mon Sep 17 00:00:00 2001 From: Michel Courtine Date: Tue, 17 Jun 2014 13:57:12 +0200 Subject: [PATCH 04/21] Added AvatarUrl property to User Model. Seems to only work with users that uploaded their own pictures. Doesn't seem to report gravatar urls... --- NGitLab/NGitLab.Tests/NGitLab.Tests.csproj | 6 ++++-- NGitLab/NGitLab/Models/User.cs | 5 ++++- NGitLab/NGitLab/NGitLab.csproj | 7 +++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj index 14553c9f..baa1cb7c 100644 --- a/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj @@ -1,5 +1,5 @@  - + Debug @@ -11,6 +11,8 @@ NGitLab.Tests v4.5 512 + 8.0.30703 + 2.0 true @@ -68,4 +70,4 @@ --> - \ No newline at end of file + diff --git a/NGitLab/NGitLab/Models/User.cs b/NGitLab/NGitLab/Models/User.cs index a24328eb..5cec5e7e 100644 --- a/NGitLab/NGitLab/Models/User.cs +++ b/NGitLab/NGitLab/Models/User.cs @@ -41,7 +41,10 @@ public class User [DataMember(Name="created_at")] public DateTime CreatedAt; - [DataMember(Name = "bio")] + [DataMember(Name="avatar_url")] + public string AvatarURL; + + [DataMember(Name = "bio")] public string Bio; [DataMember(Name="dark_scheme")] diff --git a/NGitLab/NGitLab/NGitLab.csproj b/NGitLab/NGitLab/NGitLab.csproj index d2f695e0..d7c3acc1 100644 --- a/NGitLab/NGitLab/NGitLab.csproj +++ b/NGitLab/NGitLab/NGitLab.csproj @@ -1,5 +1,5 @@  - + Debug @@ -11,6 +11,9 @@ NGitLab v4.5 512 + 8.0.30703 + 2.0 + 2.0 true @@ -92,4 +95,4 @@ --> - \ No newline at end of file + From 0bb29cec091055c8c1489e45a73e9dc29ac0a019 Mon Sep 17 00:00:00 2001 From: Michel Courtine Date: Tue, 17 Jun 2014 14:00:45 +0200 Subject: [PATCH 05/21] Fixed spacing --- NGitLab/NGitLab/Models/User.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NGitLab/NGitLab/Models/User.cs b/NGitLab/NGitLab/Models/User.cs index 5cec5e7e..47c31022 100644 --- a/NGitLab/NGitLab/Models/User.cs +++ b/NGitLab/NGitLab/Models/User.cs @@ -41,8 +41,8 @@ public class User [DataMember(Name="created_at")] public DateTime CreatedAt; - [DataMember(Name="avatar_url")] - public string AvatarURL; + [DataMember(Name="avatar_url")] + public string AvatarURL; [DataMember(Name = "bio")] public string Bio; From bb6a529d80b602a43e9859b88d15930e961976f5 Mon Sep 17 00:00:00 2001 From: Michel Courtine Date: Tue, 17 Jun 2014 14:02:28 +0200 Subject: [PATCH 06/21] Fixed spacing again... --- NGitLab/NGitLab/Models/User.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab/NGitLab/Models/User.cs b/NGitLab/NGitLab/Models/User.cs index 47c31022..b7f65a37 100644 --- a/NGitLab/NGitLab/Models/User.cs +++ b/NGitLab/NGitLab/Models/User.cs @@ -44,7 +44,7 @@ public class User [DataMember(Name="avatar_url")] public string AvatarURL; - [DataMember(Name = "bio")] + [DataMember(Name = "bio")] public string Bio; [DataMember(Name="dark_scheme")] From a63c5336c0d4a9430fabee9406f34d3950ddf1b5 Mon Sep 17 00:00:00 2001 From: Michel Courtine Date: Tue, 17 Jun 2014 13:57:12 +0200 Subject: [PATCH 07/21] Added AvatarUrl property to User Model, set ToolsVersion to 4.0 in NGitLab.csproj & NGitLab.Tests.csproj to support Xamarin --- NGitLab/NGitLab.Tests/NGitLab.Tests.csproj | 6 ++++-- NGitLab/NGitLab/Models/User.cs | 3 +++ NGitLab/NGitLab/NGitLab.csproj | 6 ++++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj index 14553c9f..baa1cb7c 100644 --- a/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj @@ -1,5 +1,5 @@  - + Debug @@ -11,6 +11,8 @@ NGitLab.Tests v4.5 512 + 8.0.30703 + 2.0 true @@ -68,4 +70,4 @@ --> - \ No newline at end of file + diff --git a/NGitLab/NGitLab/Models/User.cs b/NGitLab/NGitLab/Models/User.cs index a24328eb..b7f65a37 100644 --- a/NGitLab/NGitLab/Models/User.cs +++ b/NGitLab/NGitLab/Models/User.cs @@ -41,6 +41,9 @@ public class User [DataMember(Name="created_at")] public DateTime CreatedAt; + [DataMember(Name="avatar_url")] + public string AvatarURL; + [DataMember(Name = "bio")] public string Bio; diff --git a/NGitLab/NGitLab/NGitLab.csproj b/NGitLab/NGitLab/NGitLab.csproj index d2f695e0..b78b430e 100644 --- a/NGitLab/NGitLab/NGitLab.csproj +++ b/NGitLab/NGitLab/NGitLab.csproj @@ -1,5 +1,5 @@  - + Debug @@ -11,6 +11,8 @@ NGitLab v4.5 512 + 8.0.30703 + 2.0 true @@ -92,4 +94,4 @@ --> - \ No newline at end of file + From 0443a77d12ffbdc163cc18e290d8bb68c75b6afb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Defi=C5=84ski?= Date: Tue, 29 Jul 2014 12:09:40 +0200 Subject: [PATCH 08/21] fix GetAll<> method --- NGitLab/NGitLab/Impl/HttpRequestor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NGitLab/NGitLab/Impl/HttpRequestor.cs b/NGitLab/NGitLab/Impl/HttpRequestor.cs index 29622a2a..dcba42ac 100644 --- a/NGitLab/NGitLab/Impl/HttpRequestor.cs +++ b/NGitLab/NGitLab/Impl/HttpRequestor.cs @@ -140,7 +140,7 @@ public bool MoveNext() if (_buffer.Count > 0) { _buffer.RemoveAt(0); - return _buffer.Count > 0; + return (_buffer.Count > 0) ? true : MoveNext(); } return false; From ae29b3cfc6c377f3e359171fae253e52e9c221a7 Mon Sep 17 00:00:00 2001 From: Krzysztof Sadowski Date: Sat, 30 Aug 2014 17:24:55 +0200 Subject: [PATCH 09/21] merge request api --- .../MergeRequest/MergeRequestClientTests.cs | 74 +++++++++++++++++++ .../MergeRequest/_MergeRequestClientTests.cs | 26 +++++++ NGitLab/NGitLab.Tests/NGitLab.Tests.csproj | 2 + NGitLab/NGitLab/GitLabClient.cs | 5 ++ NGitLab/NGitLab/IMergeRequestClient.cs | 19 +++++ NGitLab/NGitLab/IMergeRequestCommentClient.cs | 6 ++ NGitLab/NGitLab/Impl/HttpRequestor.cs | 3 +- NGitLab/NGitLab/Impl/MergeRequestClient.cs | 58 +++++++++++++++ NGitLab/NGitLab/Models/Comment.cs | 14 ++++ NGitLab/NGitLab/Models/MergeCommitMessage.cs | 11 +++ NGitLab/NGitLab/Models/MergeRequest.cs | 40 ++++++++-- NGitLab/NGitLab/Models/MergeRequestCreate.cs | 23 ++++++ NGitLab/NGitLab/Models/MergeRequestUpdate.cs | 31 ++++++++ NGitLab/NGitLab/NGitLab.csproj | 7 ++ 14 files changed, 313 insertions(+), 6 deletions(-) create mode 100644 NGitLab/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs create mode 100644 NGitLab/NGitLab.Tests/MergeRequest/_MergeRequestClientTests.cs create mode 100644 NGitLab/NGitLab/IMergeRequestClient.cs create mode 100644 NGitLab/NGitLab/IMergeRequestCommentClient.cs create mode 100644 NGitLab/NGitLab/Impl/MergeRequestClient.cs create mode 100644 NGitLab/NGitLab/Models/Comment.cs create mode 100644 NGitLab/NGitLab/Models/MergeCommitMessage.cs create mode 100644 NGitLab/NGitLab/Models/MergeRequestCreate.cs create mode 100644 NGitLab/NGitLab/Models/MergeRequestUpdate.cs diff --git a/NGitLab/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs b/NGitLab/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs new file mode 100644 index 00000000..fdf62c17 --- /dev/null +++ b/NGitLab/NGitLab.Tests/MergeRequest/MergeRequestClientTests.cs @@ -0,0 +1,74 @@ +using System.Linq; +using NGitLab.Models; +using NUnit.Framework; + +namespace NGitLab.Tests.MergeRequest +{ + public class MergeRequestClientTests + { + private readonly IMergeRequestClient _mergeRequest; + + public MergeRequestClientTests() + { + _mergeRequest = _MergeRequestClientTests.MergeRequestClient; + } + + [Test] + public void GetAllMergeRequests() + { + var mergeRequests = _mergeRequest.All.ToArray(); + CollectionAssert.IsNotEmpty(mergeRequests); + } + + [Test] + public void GetAllMergeRequestsInCertainState() + { + var mergeRequests = _mergeRequest.AllInState(MergeRequestState.opened).ToArray(); + CollectionAssert.IsNotEmpty(mergeRequests); + } + + [Test] + public void GetMergeRequestById() + { + const int mergeReqestId = 5; + Assert.AreEqual(mergeReqestId, _mergeRequest[mergeReqestId].Id); + } + + [Test] + public void CreateMergeRequest() + { + var mergeRequest = _mergeRequest.Create(new MergeRequestCreate + { + Title = "Merge my-super-feature into master", + SourceBranch = "my-super-feature", + TargetBranch = "master" + }); + + Assert.That(mergeRequest, Is.Not.Null); + } + + [Test] + public void UpdateMergeRequest() + { + var mergeRequest = _mergeRequest.Update(5, new MergeRequestUpdate + { + Title = "Merge my-super-feature into master", + TargetBranch = "my-super-feature", + SourceBranch = "master", + NewState = MergeRequestStateEvent.close.ToString() + }); + + Assert.That(mergeRequest, Is.Not.Null); + } + + [Test] + public void AcceptMergeRequest() + { + var mergeRequest = _mergeRequest.Accept( + mergeRequestId: 6, + message: new MergeCommitMessage {Message = "Merge my-super-feature into master"}); + + Assert.That(mergeRequest.State, Is.EqualTo(MergeRequestState.merged.ToString())); + } + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab.Tests/MergeRequest/_MergeRequestClientTests.cs b/NGitLab/NGitLab.Tests/MergeRequest/_MergeRequestClientTests.cs new file mode 100644 index 00000000..1c6c40b2 --- /dev/null +++ b/NGitLab/NGitLab.Tests/MergeRequest/_MergeRequestClientTests.cs @@ -0,0 +1,26 @@ +using System.Linq; +using NGitLab.Models; +using NUnit.Framework; + +namespace NGitLab.Tests.MergeRequest +{ + // ReSharper disable once InconsistentNaming + [SetUpFixture] + public class _MergeRequestClientTests + { + public static IMergeRequestClient MergeRequestClient; + public static Project Project; + + [SetUp] + public void SetUp() + { + Project = Config.Connect().Projects.Owned.First(project => project.Name == "mergeme"); + MergeRequestClient = Config.Connect().GetMergeRequest(Project.Id); + } + + [TearDown] + public void TearDown() + { + } + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj index baa1cb7c..9fd987fd 100644 --- a/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj @@ -42,6 +42,8 @@ + + diff --git a/NGitLab/NGitLab/GitLabClient.cs b/NGitLab/NGitLab/GitLabClient.cs index 51837525..c1e22c6b 100644 --- a/NGitLab/NGitLab/GitLabClient.cs +++ b/NGitLab/NGitLab/GitLabClient.cs @@ -25,5 +25,10 @@ public IRepositoryClient GetRepository(int projectId) { return new RepositoryClient(_api, projectId); } + + public IMergeRequestClient GetMergeRequest(int projectId) + { + return new MergeRequestClient(_api, projectId); + } } } \ No newline at end of file diff --git a/NGitLab/NGitLab/IMergeRequestClient.cs b/NGitLab/NGitLab/IMergeRequestClient.cs new file mode 100644 index 00000000..0a580b89 --- /dev/null +++ b/NGitLab/NGitLab/IMergeRequestClient.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using NGitLab.Impl; +using NGitLab.Models; + +namespace NGitLab +{ + public interface IMergeRequestClient + { + IEnumerable All { get; } + IEnumerable AllInState(MergeRequestState state); + MergeRequest this[int id] { get; } + + MergeRequest Create(MergeRequestCreate mergeRequest); + MergeRequest Update(int mergeRequestId, MergeRequestUpdate mergeRequest); + MergeRequest Accept(int mergeRequestId, MergeCommitMessage message); + + IMergeRequestCommentClient Comments { get; } + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab/IMergeRequestCommentClient.cs b/NGitLab/NGitLab/IMergeRequestCommentClient.cs new file mode 100644 index 00000000..525cdddf --- /dev/null +++ b/NGitLab/NGitLab/IMergeRequestCommentClient.cs @@ -0,0 +1,6 @@ +namespace NGitLab.Impl +{ + public interface IMergeRequestCommentClient + { + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab/Impl/HttpRequestor.cs b/NGitLab/NGitLab/Impl/HttpRequestor.cs index dcba42ac..9b5aef80 100644 --- a/NGitLab/NGitLab/Impl/HttpRequestor.cs +++ b/NGitLab/NGitLab/Impl/HttpRequestor.cs @@ -172,7 +172,8 @@ private void SubmitData(WebRequest request) using (var writer = new StreamWriter(request.GetRequestStream())) { - writer.Write(SimpleJson.SerializeObject(_data)); + var data = SimpleJson.SerializeObject(_data); + writer.Write(data); writer.Flush(); writer.Close(); } diff --git a/NGitLab/NGitLab/Impl/MergeRequestClient.cs b/NGitLab/NGitLab/Impl/MergeRequestClient.cs new file mode 100644 index 00000000..e8662147 --- /dev/null +++ b/NGitLab/NGitLab/Impl/MergeRequestClient.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; +using NGitLab.Models; + +namespace NGitLab.Impl +{ + public class MergeRequestClient : IMergeRequestClient + { + private readonly API _api; + private readonly string _projectPath; + + public MergeRequestClient(API api, int projectId) + { + _api = api; + _projectPath = Project.Url + "/" + projectId; + } + + public IEnumerable All + { + get { return _api.Get().GetAll(_projectPath + "/merge_requests"); } + } + + public IEnumerable AllInState(MergeRequestState state) + { + return _api.Get().GetAll(_projectPath + "/merge_requests?state=" + state); + } + + public MergeRequest this[int id] + { + get { return _api.Get().To(_projectPath + "/merge_request/" + id); } + } + + public MergeRequest Create(MergeRequestCreate mergeRequest) + { + return _api + .Post().With(mergeRequest) + .To(_projectPath + "/merge_requests"); + } + + public MergeRequest Update(int mergeRequestId, MergeRequestUpdate mergeRequest) + { + return _api + .Put().With(mergeRequest) + .To(_projectPath + "/merge_request/" + mergeRequestId); + } + + public MergeRequest Accept(int mergeRequestId, MergeCommitMessage message) + { + return _api + .Put().With(message) + .To(_projectPath + "/merge_request/" + mergeRequestId + "/merge"); + } + + public IMergeRequestCommentClient Comments + { + get { return null; } + } + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab/Models/Comment.cs b/NGitLab/NGitLab/Models/Comment.cs new file mode 100644 index 00000000..e0166196 --- /dev/null +++ b/NGitLab/NGitLab/Models/Comment.cs @@ -0,0 +1,14 @@ +using System.Runtime.Serialization; + +namespace NGitLab.Models +{ + [DataContract] + public class Comment + { + [DataMember(Name = "note")] + public string Note { get; set; } + + [DataMember(Name = "author")] + public User Author { get; set; } + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab/Models/MergeCommitMessage.cs b/NGitLab/NGitLab/Models/MergeCommitMessage.cs new file mode 100644 index 00000000..9a6ca899 --- /dev/null +++ b/NGitLab/NGitLab/Models/MergeCommitMessage.cs @@ -0,0 +1,11 @@ +using System.Runtime.Serialization; + +namespace NGitLab.Models +{ + [DataContract] + public class MergeCommitMessage + { + [DataMember(Name = "merge_commit_message")] + public string Message; + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab/Models/MergeRequest.cs b/NGitLab/NGitLab/Models/MergeRequest.cs index 945b10a6..87f4e885 100644 --- a/NGitLab/NGitLab/Models/MergeRequest.cs +++ b/NGitLab/NGitLab/Models/MergeRequest.cs @@ -1,20 +1,47 @@ -using System.Runtime.Serialization; +using System; +using System.Runtime.Serialization; namespace NGitLab.Models { + [DataContract] public class MergeRequest { public const string Url = "/merge_requests"; + [DataMember(Name = "id")] public int Id; + + [DataMember(Name = "iid")] public int Iid; - public string Title; + + + [DataMember(Name = "state")] public string State; - public bool Closed; - public bool Merged; - public User Author; + + [DataMember(Name = "title")] + public string Title; + + [DataMember(Name = "assignee")] public User Assignee; + [DataMember(Name = "author")] + public User Author; + + [DataMember(Name = "created_at")] + public DateTime CreatedAt; + + [DataMember(Name = "description")] + public string Description; + + [DataMember(Name = "downvotes")] + public int Downvotes; + + [DataMember(Name = "upvotes")] + public int Upvotes; + + [DataMember(Name = "updated_at")] + public DateTime UpvotedAt; + [DataMember(Name="target_branch")] public string TargetBranch; @@ -26,5 +53,8 @@ public class MergeRequest [DataMember(Name="source_project_id")] public int SourceProjectId; + + [DataMember(Name = "target_project_id")] + public int TargetProjectId; } } \ No newline at end of file diff --git a/NGitLab/NGitLab/Models/MergeRequestCreate.cs b/NGitLab/NGitLab/Models/MergeRequestCreate.cs new file mode 100644 index 00000000..2b3fbedb --- /dev/null +++ b/NGitLab/NGitLab/Models/MergeRequestCreate.cs @@ -0,0 +1,23 @@ +using System.Runtime.Serialization; + +namespace NGitLab.Models +{ + [DataContract] + public class MergeRequestCreate + { + [DataMember(Name="source_branch")] + public string SourceBranch; + + [DataMember(Name="target_branch")] + public string TargetBranch; + + [DataMember(Name="assignee_id")] + public int? AssigneeId; + + [DataMember(Name="title")] + public string Title; + + [DataMember(Name="target_project_id")] + public int? TargetProjectId; + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab/Models/MergeRequestUpdate.cs b/NGitLab/NGitLab/Models/MergeRequestUpdate.cs new file mode 100644 index 00000000..bf7d0c7d --- /dev/null +++ b/NGitLab/NGitLab/Models/MergeRequestUpdate.cs @@ -0,0 +1,31 @@ +using System.Runtime.Serialization; + +namespace NGitLab.Models +{ + [DataContract] + public class MergeRequestUpdate + { + [DataMember(Name = "source_branch")] + public string SourceBranch; + + [DataMember(Name = "target_branch")] + public string TargetBranch; + + [DataMember(Name = "assignee_id")] + public int? AssigneeId; + + [DataMember(Name = "title")] + public string Title; + + [DataMember(Name = "state_event")] + public string NewState; + } + + // ReSharper disable InconsistentNaming + public enum MergeRequestStateEvent + { + close, + reopen, + merge + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab/NGitLab.csproj b/NGitLab/NGitLab/NGitLab.csproj index b78b430e..102ed925 100644 --- a/NGitLab/NGitLab/NGitLab.csproj +++ b/NGitLab/NGitLab/NGitLab.csproj @@ -44,8 +44,11 @@ + + + @@ -59,14 +62,18 @@ + + + + From 01f56a28cd6f151f5982c4b7bf6bf0e31e96611a Mon Sep 17 00:00:00 2001 From: Krzysztof Sadowski Date: Sun, 31 Aug 2014 16:55:54 +0200 Subject: [PATCH 10/21] merge request comments api --- .../MergeRequestCommentsClientTests.cs | 34 +++++++++++++++++++ NGitLab/NGitLab.Tests/NGitLab.Tests.csproj | 3 +- NGitLab/NGitLab/IMergeRequestClient.cs | 3 +- NGitLab/NGitLab/IMergeRequestCommentClient.cs | 8 ++++- NGitLab/NGitLab/Impl/MergeRequestClient.cs | 4 +-- .../NGitLab/Impl/MergeRequestCommentClient.cs | 27 +++++++++++++++ NGitLab/NGitLab/Models/MergeRequestComment.cs | 11 ++++++ NGitLab/NGitLab/NGitLab.csproj | 4 ++- 8 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 NGitLab/NGitLab.Tests/MergeRequest/MergeRequestCommentsClientTests.cs create mode 100644 NGitLab/NGitLab/Impl/MergeRequestCommentClient.cs create mode 100644 NGitLab/NGitLab/Models/MergeRequestComment.cs diff --git a/NGitLab/NGitLab.Tests/MergeRequest/MergeRequestCommentsClientTests.cs b/NGitLab/NGitLab.Tests/MergeRequest/MergeRequestCommentsClientTests.cs new file mode 100644 index 00000000..413a4e41 --- /dev/null +++ b/NGitLab/NGitLab.Tests/MergeRequest/MergeRequestCommentsClientTests.cs @@ -0,0 +1,34 @@ +using System.Linq; +using NGitLab.Models; +using NUnit.Framework; + +namespace NGitLab.Tests.MergeRequest +{ + public class MergeRequestCommentsClientTests + { + private readonly IMergeRequestCommentClient _mergeRequestComments; + + public MergeRequestCommentsClientTests() + { + _mergeRequestComments = _MergeRequestClientTests.MergeRequestClient.Comments(5); + } + + [Test] + public void GetAllComments() + { + var comments = _mergeRequestComments.All.ToArray(); + CollectionAssert.IsNotEmpty(comments); + } + + [Test] + public void AddCommentToMergeRequest() + { + const string commentMessage = "note"; + var newComment = new MergeRequestComment {Note = commentMessage}; + + var mergeRequest = _mergeRequestComments.Add(newComment); + + Assert.That(mergeRequest.Note, Is.EqualTo(commentMessage)); + } + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj index 9fd987fd..b59cced2 100644 --- a/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj @@ -43,6 +43,7 @@ + @@ -72,4 +73,4 @@ --> - + \ No newline at end of file diff --git a/NGitLab/NGitLab/IMergeRequestClient.cs b/NGitLab/NGitLab/IMergeRequestClient.cs index 0a580b89..27eda553 100644 --- a/NGitLab/NGitLab/IMergeRequestClient.cs +++ b/NGitLab/NGitLab/IMergeRequestClient.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using NGitLab.Impl; using NGitLab.Models; namespace NGitLab @@ -14,6 +13,6 @@ public interface IMergeRequestClient MergeRequest Update(int mergeRequestId, MergeRequestUpdate mergeRequest); MergeRequest Accept(int mergeRequestId, MergeCommitMessage message); - IMergeRequestCommentClient Comments { get; } + IMergeRequestCommentClient Comments(int mergeRequestId); } } \ No newline at end of file diff --git a/NGitLab/NGitLab/IMergeRequestCommentClient.cs b/NGitLab/NGitLab/IMergeRequestCommentClient.cs index 525cdddf..3e4912fa 100644 --- a/NGitLab/NGitLab/IMergeRequestCommentClient.cs +++ b/NGitLab/NGitLab/IMergeRequestCommentClient.cs @@ -1,6 +1,12 @@ -namespace NGitLab.Impl +using System.Collections.Generic; +using NGitLab.Models; + +namespace NGitLab { public interface IMergeRequestCommentClient { + IEnumerable All { get; } + + Comment Add(MergeRequestComment comment); } } \ No newline at end of file diff --git a/NGitLab/NGitLab/Impl/MergeRequestClient.cs b/NGitLab/NGitLab/Impl/MergeRequestClient.cs index e8662147..27932530 100644 --- a/NGitLab/NGitLab/Impl/MergeRequestClient.cs +++ b/NGitLab/NGitLab/Impl/MergeRequestClient.cs @@ -50,9 +50,9 @@ public MergeRequest Accept(int mergeRequestId, MergeCommitMessage message) .To(_projectPath + "/merge_request/" + mergeRequestId + "/merge"); } - public IMergeRequestCommentClient Comments + public IMergeRequestCommentClient Comments(int mergeRequestId) { - get { return null; } + return new MergeRequestCommentClient(_api, _projectPath, mergeRequestId); } } } \ No newline at end of file diff --git a/NGitLab/NGitLab/Impl/MergeRequestCommentClient.cs b/NGitLab/NGitLab/Impl/MergeRequestCommentClient.cs new file mode 100644 index 00000000..b74b9b99 --- /dev/null +++ b/NGitLab/NGitLab/Impl/MergeRequestCommentClient.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using NGitLab.Models; + +namespace NGitLab.Impl +{ + public class MergeRequestCommentClient : IMergeRequestCommentClient + { + private readonly API _api; + private readonly string _commentsPath; + + public MergeRequestCommentClient(API api, string projectPath, int mergeRequestId) + { + _api = api; + _commentsPath = projectPath + "/merge_request/" + mergeRequestId + "/comments"; + } + + public IEnumerable All + { + get { return _api.Get().GetAll(_commentsPath); } + } + + public Comment Add(MergeRequestComment comment) + { + return _api.Post().With(comment).To(_commentsPath); + } + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab/Models/MergeRequestComment.cs b/NGitLab/NGitLab/Models/MergeRequestComment.cs new file mode 100644 index 00000000..eebc302b --- /dev/null +++ b/NGitLab/NGitLab/Models/MergeRequestComment.cs @@ -0,0 +1,11 @@ +using System.Runtime.Serialization; + +namespace NGitLab.Models +{ + [DataContract] + public class MergeRequestComment + { + [DataMember(Name = "note")] + public string Note; + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab/NGitLab.csproj b/NGitLab/NGitLab/NGitLab.csproj index 102ed925..aeb65b92 100644 --- a/NGitLab/NGitLab/NGitLab.csproj +++ b/NGitLab/NGitLab/NGitLab.csproj @@ -49,6 +49,7 @@ + @@ -71,6 +72,7 @@ + @@ -101,4 +103,4 @@ --> - + \ No newline at end of file From 089311fd60c749b57f36291574a11134f9b713b8 Mon Sep 17 00:00:00 2001 From: "Krzysztof.Sadowski" Date: Fri, 12 Sep 2014 11:31:54 +0200 Subject: [PATCH 11/21] branch removal api --- .../RepositoryClient/BranchClientTests.cs | 7 +++++ NGitLab/NGitLab/IBranchClient.cs | 1 + NGitLab/NGitLab/Impl/BranchClient.cs | 6 ++++ NGitLab/NGitLab/Models/BranchRemovalStatus.cs | 29 +++++++++++++++++++ NGitLab/NGitLab/NGitLab.csproj | 1 + 5 files changed, 44 insertions(+) create mode 100644 NGitLab/NGitLab/Models/BranchRemovalStatus.cs diff --git a/NGitLab/NGitLab.Tests/RepositoryClient/BranchClientTests.cs b/NGitLab/NGitLab.Tests/RepositoryClient/BranchClientTests.cs index ec400efd..576ac440 100644 --- a/NGitLab/NGitLab.Tests/RepositoryClient/BranchClientTests.cs +++ b/NGitLab/NGitLab.Tests/RepositoryClient/BranchClientTests.cs @@ -25,5 +25,12 @@ public void GetByName() Assert.IsNotNull(branch); Assert.IsNotNull(branch.Name); } + + [Test] + public void DeleteByName() + { + var result = _branches.Delete("merge-me-to-master"); + Assert.That(result.Succeed); + } } } \ No newline at end of file diff --git a/NGitLab/NGitLab/IBranchClient.cs b/NGitLab/NGitLab/IBranchClient.cs index dbd85735..92d3934d 100644 --- a/NGitLab/NGitLab/IBranchClient.cs +++ b/NGitLab/NGitLab/IBranchClient.cs @@ -10,5 +10,6 @@ public interface IBranchClient Branch Protect(string name); Branch Unprotect(string name); Branch Create(BranchCreate branch); + BranchRemovalStatus Delete(string name); } } \ No newline at end of file diff --git a/NGitLab/NGitLab/Impl/BranchClient.cs b/NGitLab/NGitLab/Impl/BranchClient.cs index c6bc87b9..ce0cea47 100644 --- a/NGitLab/NGitLab/Impl/BranchClient.cs +++ b/NGitLab/NGitLab/Impl/BranchClient.cs @@ -38,5 +38,11 @@ public Branch Create(BranchCreate branch) { return _api.Post().With(branch).To(_repoPath + "/branches"); } + + public BranchRemovalStatus Delete(string name) + { + var errorMessage = _api.Delete().To(_repoPath + "/branches/" + name); + return BranchRemovalStatus.FromReponseMessage(errorMessage); + } } } \ No newline at end of file diff --git a/NGitLab/NGitLab/Models/BranchRemovalStatus.cs b/NGitLab/NGitLab/Models/BranchRemovalStatus.cs new file mode 100644 index 00000000..96d56cca --- /dev/null +++ b/NGitLab/NGitLab/Models/BranchRemovalStatus.cs @@ -0,0 +1,29 @@ +namespace NGitLab.Models +{ + public class BranchRemovalStatus + { + public readonly bool Succeed; + public readonly string Message; + + public static BranchRemovalStatus FromReponseMessage(string message) + { + return Success(); + } + + public static BranchRemovalStatus Success() + { + return new BranchRemovalStatus(true, string.Empty); + } + + public static BranchRemovalStatus Failure(string message) + { + return new BranchRemovalStatus(false, message); + } + + private BranchRemovalStatus(bool succeed, string message) + { + Succeed = succeed; + Message = message; + } + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab/NGitLab.csproj b/NGitLab/NGitLab/NGitLab.csproj index aeb65b92..d6d8b917 100644 --- a/NGitLab/NGitLab/NGitLab.csproj +++ b/NGitLab/NGitLab/NGitLab.csproj @@ -63,6 +63,7 @@ + From 1e7a5de7f335c62d6c356dfdb82fb0a294f0cd4f Mon Sep 17 00:00:00 2001 From: "petro.margaritis" Date: Mon, 3 Nov 2014 13:17:44 +1100 Subject: [PATCH 12/21] added IssueClient with method to retrieve authenticated users owned issues, and a ForProject method to retreive a projects issues. --- NGitLab/NGitLab/GitLabClient.cs | 2 ++ NGitLab/NGitLab/IIssueClient.cs | 18 ++++++++++++ NGitLab/NGitLab/Impl/IssueClient.cs | 30 +++++++++++++++++++ NGitLab/NGitLab/Models/Assignee.cs | 27 +++++++++++++++++ NGitLab/NGitLab/Models/Author.cs | 27 +++++++++++++++++ NGitLab/NGitLab/Models/Issue.cs | 45 +++++++++++++++++++++++++++++ NGitLab/NGitLab/Models/Milestone.cs | 30 +++++++++++++++++++ NGitLab/NGitLab/NGitLab.csproj | 6 ++++ 8 files changed, 185 insertions(+) create mode 100644 NGitLab/NGitLab/IIssueClient.cs create mode 100644 NGitLab/NGitLab/Impl/IssueClient.cs create mode 100644 NGitLab/NGitLab/Models/Assignee.cs create mode 100644 NGitLab/NGitLab/Models/Author.cs create mode 100644 NGitLab/NGitLab/Models/Issue.cs create mode 100644 NGitLab/NGitLab/Models/Milestone.cs diff --git a/NGitLab/NGitLab/GitLabClient.cs b/NGitLab/NGitLab/GitLabClient.cs index c1e22c6b..d2405901 100644 --- a/NGitLab/NGitLab/GitLabClient.cs +++ b/NGitLab/NGitLab/GitLabClient.cs @@ -9,6 +9,7 @@ private GitLabClient(string hostUrl, string apiToken) _api = new API(hostUrl, apiToken); Users = new UserClient(_api); Projects = new ProjectClient(_api); + Issues = new IssueClient(_api); } public static GitLabClient Connect(string hostUrl, string apiToken) @@ -20,6 +21,7 @@ public static GitLabClient Connect(string hostUrl, string apiToken) public readonly IUserClient Users; public readonly IProjectClient Projects; + public readonly IIssueClient Issues; public IRepositoryClient GetRepository(int projectId) { diff --git a/NGitLab/NGitLab/IIssueClient.cs b/NGitLab/NGitLab/IIssueClient.cs new file mode 100644 index 00000000..1aa00989 --- /dev/null +++ b/NGitLab/NGitLab/IIssueClient.cs @@ -0,0 +1,18 @@ +using System.Collections.Generic; +using NGitLab.Models; + +namespace NGitLab +{ + public interface IIssueClient + { + /// + /// Get a list of all project issues + /// + IEnumerable Owned { get; } + + /// + /// Get a list of issues for the specified project. + /// + IEnumerable ForProject(int projectId); + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab/Impl/IssueClient.cs b/NGitLab/NGitLab/Impl/IssueClient.cs new file mode 100644 index 00000000..1225a51c --- /dev/null +++ b/NGitLab/NGitLab/Impl/IssueClient.cs @@ -0,0 +1,30 @@ +using System.Collections.Generic; +using NGitLab.Models; + +namespace NGitLab.Impl +{ + public class IssueClient : IIssueClient + { + private const string IssuesUrl = "/issues"; + private const string ProjectIssuesUrl = "/projects/{0}/issues"; + private readonly API _api; + + public IssueClient(API api) + { + _api = api; + } + + public IEnumerable Owned + { + get + { + return _api.Get().GetAll(IssuesUrl); + } + } + + public IEnumerable ForProject(int projectId) + { + return _api.Get().GetAll(string.Format(ProjectIssuesUrl, projectId)); + } + } +} diff --git a/NGitLab/NGitLab/Models/Assignee.cs b/NGitLab/NGitLab/Models/Assignee.cs new file mode 100644 index 00000000..286956f7 --- /dev/null +++ b/NGitLab/NGitLab/Models/Assignee.cs @@ -0,0 +1,27 @@ +using System; +using System.Runtime.Serialization; + +namespace NGitLab.Models +{ + [DataContract] + public class Assignee + { + [DataMember(Name = "id")] + public int Id; + + [DataMember(Name = "username")] + public string Username; + + [DataMember(Name = "email")] + public string Email; + + [DataMember(Name = "name")] + public string Name; + + [DataMember(Name = "state")] + public string State; + + [DataMember(Name = "created_at")] + public DateTime CreatedAt; + } +} diff --git a/NGitLab/NGitLab/Models/Author.cs b/NGitLab/NGitLab/Models/Author.cs new file mode 100644 index 00000000..99fa31fa --- /dev/null +++ b/NGitLab/NGitLab/Models/Author.cs @@ -0,0 +1,27 @@ +using System; +using System.Runtime.Serialization; + +namespace NGitLab.Models +{ + [DataContract] + public class Author + { + [DataMember(Name = "id")] + public int Id; + + [DataMember(Name = "username")] + public string Username; + + [DataMember(Name = "email")] + public string Email; + + [DataMember(Name = "name")] + public string Name; + + [DataMember(Name = "state")] + public string State; + + [DataMember(Name = "created_at")] + public DateTime CreatedAt; + } +} diff --git a/NGitLab/NGitLab/Models/Issue.cs b/NGitLab/NGitLab/Models/Issue.cs new file mode 100644 index 00000000..b79e1f9c --- /dev/null +++ b/NGitLab/NGitLab/Models/Issue.cs @@ -0,0 +1,45 @@ +using System; +using System.Runtime.Serialization; + +namespace NGitLab.Models +{ + [DataContract] + public class Issue + { + [DataMember(Name = "id")] + public int Id; + + [DataMember(Name = "iid")] + public int IssueId; + + [DataMember(Name = "project_id")] + public int ProjectId; + + [DataMember(Name = "title")] + public string Title; + + [DataMember(Name = "description")] + public string Description; + + [DataMember(Name = "labels")] + public string[] Labels; + + [DataMember(Name = "milestone")] + public Milestone Milestone; + + [DataMember(Name = "assignee")] + public Assignee Assignee; + + [DataMember(Name = "author")] + public Author Author; + + [DataMember(Name = "state")] + public string State; + + [DataMember(Name = "created_at")] + public DateTime CreatedAt; + + [DataMember(Name = "updated_at")] + public DateTime UpdatedAt; + } +} diff --git a/NGitLab/NGitLab/Models/Milestone.cs b/NGitLab/NGitLab/Models/Milestone.cs new file mode 100644 index 00000000..58c544f4 --- /dev/null +++ b/NGitLab/NGitLab/Models/Milestone.cs @@ -0,0 +1,30 @@ +using System; +using System.Runtime.Serialization; + +namespace NGitLab.Models +{ + [DataContract] + public class Milestone + { + [DataMember(Name = "id")] + public int Id; + + [DataMember(Name = "title")] + public string Title; + + [DataMember(Name = "description")] + public string Description; + + [DataMember(Name = "due_date")] + public string DueDate; + + [DataMember(Name = "state")] + public string State; + + [DataMember(Name = "created_at")] + public DateTime CreatedAt; + + [DataMember(Name = "updated_at")] + public DateTime UpdatedAt; + } +} diff --git a/NGitLab/NGitLab/NGitLab.csproj b/NGitLab/NGitLab/NGitLab.csproj index aeb65b92..96893d8c 100644 --- a/NGitLab/NGitLab/NGitLab.csproj +++ b/NGitLab/NGitLab/NGitLab.csproj @@ -44,10 +44,12 @@ + + @@ -61,6 +63,8 @@ + + @@ -70,12 +74,14 @@ + + From 7ea67bb271c4a94d571220cab78a0928b435d9b3 Mon Sep 17 00:00:00 2001 From: Adrian Cockburn Date: Thu, 11 Dec 2014 23:17:58 +0000 Subject: [PATCH 13/21] automatically handle decompression of gzipped responses --- NGitLab/NGitLab/Impl/HttpRequestor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/NGitLab/NGitLab/Impl/HttpRequestor.cs b/NGitLab/NGitLab/Impl/HttpRequestor.cs index 9b5aef80..dbce9d57 100644 --- a/NGitLab/NGitLab/Impl/HttpRequestor.cs +++ b/NGitLab/NGitLab/Impl/HttpRequestor.cs @@ -191,9 +191,10 @@ private WebRequest SetupConnection(Uri url) private static WebRequest SetupConnection(Uri url, MethodType methodType) { - var request = WebRequest.Create(url); + var request = (HttpWebRequest)WebRequest.Create(url); request.Method = methodType.ToString().ToUpperInvariant(); request.Headers.Add("Accept-Encoding", "gzip"); + request.AutomaticDecompression = DecompressionMethods.GZip; return request; } From cec0f5d17a7e901ad89b05bd423243d6344561d2 Mon Sep 17 00:00:00 2001 From: Adrian Cockburn Date: Tue, 16 Dec 2014 16:59:45 +0000 Subject: [PATCH 14/21] add path to project model --- NGitLab/NGitLab.Tests/NGitLab.Tests.csproj | 2 +- NGitLab/NGitLab.sln | 6 ++---- NGitLab/NGitLab/Models/ProjectCreate.cs | 5 ++++- NGitLab/NGitLab/NGitLab.csproj | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj b/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj index b59cced2..52a406a4 100644 --- a/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj +++ b/NGitLab/NGitLab.Tests/NGitLab.Tests.csproj @@ -9,7 +9,7 @@ Properties NGitLab.Tests NGitLab.Tests - v4.5 + v4.0 512 8.0.30703 2.0 diff --git a/NGitLab/NGitLab.sln b/NGitLab/NGitLab.sln index b3fd7dea..21163ad8 100644 --- a/NGitLab/NGitLab.sln +++ b/NGitLab/NGitLab.sln @@ -1,8 +1,6 @@  -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0.21005.1 -MinimumVisualStudioVersion = 10.0.40219.1 +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NGitLab", "NGitLab\NGitLab.csproj", "{EB69C6AD-64A6-4FE5-880C-38607A2430FB}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NGitLab.Tests", "NGitLab.Tests\NGitLab.Tests.csproj", "{B1C9D8F6-22BC-4401-BD7B-E99C62A8E685}" diff --git a/NGitLab/NGitLab/Models/ProjectCreate.cs b/NGitLab/NGitLab/Models/ProjectCreate.cs index 5563a5ae..c8dca21b 100644 --- a/NGitLab/NGitLab/Models/ProjectCreate.cs +++ b/NGitLab/NGitLab/Models/ProjectCreate.cs @@ -17,7 +17,10 @@ public class ProjectCreate [DataMember(Name = "description")] public string Description; - [DataMember(Name = "issues_enabled")] + [DataMember(Name = "path")] + public string Path; + + [DataMember(Name = "issues_enabled")] public bool IssuesEnabled; [DataMember(Name = "wall_enabled")] diff --git a/NGitLab/NGitLab/NGitLab.csproj b/NGitLab/NGitLab/NGitLab.csproj index 96893d8c..d00a34ce 100644 --- a/NGitLab/NGitLab/NGitLab.csproj +++ b/NGitLab/NGitLab/NGitLab.csproj @@ -9,7 +9,7 @@ Properties NGitLab NGitLab - v4.5 + v4.0 512 8.0.30703 2.0 From fc98a1ca3169410c0077e84126bcc4c3621b1c8a Mon Sep 17 00:00:00 2001 From: Scooletz Date: Sun, 18 Jan 2015 22:01:34 +0100 Subject: [PATCH 15/21] PSake build & proper spec --- .gitignore | 1 + NGitLab.nuspec | 2 +- build.ps1 | 57 + tools/psake/README.markdown | 58 + tools/psake/chocolateyInstall.ps1 | 19 + tools/psake/en-US/psake.psm1-help.xml | 2130 +++++++++++++++++ tools/psake/examples/checkvariables.ps1 | 33 + tools/psake/examples/continueonerror.ps1 | 14 + tools/psake/examples/default.ps1 | 23 + .../examples/formattaskname_scriptblock.ps1 | 24 + .../psake/examples/formattaskname_string.ps1 | 21 + tools/psake/examples/msbuild40.ps1 | 8 + tools/psake/examples/nested.ps1 | 17 + tools/psake/examples/nested/nested1.ps1 | 1 + tools/psake/examples/nested/nested2.ps1 | 1 + tools/psake/examples/parameters.ps1 | 9 + .../build.Release.Version.bat | 3 + .../passingParametersString/parameters.ps1 | 22 + tools/psake/examples/preandpostaction.ps1 | 13 + tools/psake/examples/preandpostcondition.ps1 | 18 + tools/psake/examples/properties.ps1 | 13 + tools/psake/examples/requiredvariables.ps1 | 14 + tools/psake/examples/tasksetupandteardown.ps1 | 17 + tools/psake/init.ps1 | 10 + tools/psake/license.txt | 20 + tools/psake/psake-config.ps1 | 21 + tools/psake/psake.cmd | 14 + tools/psake/psake.ps1 | 51 + tools/psake/psake.psm1 | 847 +++++++ 29 files changed, 3480 insertions(+), 1 deletion(-) create mode 100644 build.ps1 create mode 100644 tools/psake/README.markdown create mode 100644 tools/psake/chocolateyInstall.ps1 create mode 100644 tools/psake/en-US/psake.psm1-help.xml create mode 100644 tools/psake/examples/checkvariables.ps1 create mode 100644 tools/psake/examples/continueonerror.ps1 create mode 100644 tools/psake/examples/default.ps1 create mode 100644 tools/psake/examples/formattaskname_scriptblock.ps1 create mode 100644 tools/psake/examples/formattaskname_string.ps1 create mode 100644 tools/psake/examples/msbuild40.ps1 create mode 100644 tools/psake/examples/nested.ps1 create mode 100644 tools/psake/examples/nested/nested1.ps1 create mode 100644 tools/psake/examples/nested/nested2.ps1 create mode 100644 tools/psake/examples/parameters.ps1 create mode 100644 tools/psake/examples/passingParametersString/build.Release.Version.bat create mode 100644 tools/psake/examples/passingParametersString/parameters.ps1 create mode 100644 tools/psake/examples/preandpostaction.ps1 create mode 100644 tools/psake/examples/preandpostcondition.ps1 create mode 100644 tools/psake/examples/properties.ps1 create mode 100644 tools/psake/examples/requiredvariables.ps1 create mode 100644 tools/psake/examples/tasksetupandteardown.ps1 create mode 100644 tools/psake/init.ps1 create mode 100644 tools/psake/license.txt create mode 100644 tools/psake/psake-config.ps1 create mode 100644 tools/psake/psake.cmd create mode 100644 tools/psake/psake.ps1 create mode 100644 tools/psake/psake.psm1 diff --git a/.gitignore b/.gitignore index b18e58bb..972130f3 100644 --- a/.gitignore +++ b/.gitignore @@ -174,3 +174,4 @@ UpgradeLog*.htm # Microsoft Fakes FakesAssemblies/ +*.nupkg diff --git a/NGitLab.nuspec b/NGitLab.nuspec index 11dfa150..d228a044 100644 --- a/NGitLab.nuspec +++ b/NGitLab.nuspec @@ -17,6 +17,6 @@ - + \ No newline at end of file diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 00000000..c1a870d4 --- /dev/null +++ b/build.ps1 @@ -0,0 +1,57 @@ +Task default -Depends Compile, Pack + +properties { + $path = ".\NGitLab\NGitLab.sln" + $thisVersion = $version +} + +Task Compile -Depends Clear, Restore { + msbuild $path /t:Build /p:Configuration=Release /verbosity:minimal + + if ($lastexitcode -ne 0) { + throw "Compilation failed" + } +} + +Task Restore { + .\NuGet.exe restore $path +} + +Task Clear { + Get-ChildItem .\ -include bin,obj -Recurse | foreach ($_) { remove-item $_.fullname -Force -Recurse } + Remove-Item *.nupkg + Remove-Item *TestResult.xml +} + +Task Tests { + # currently only with a running vm :( + # if you have an idea for a proper tests, please issue a pull request + + if ($lastexitcode -ne 0) { + throw "Tests failed" + } +} + +Task Pack { + $specs = @("NGitLab") + + Foreach ($spec in $specs){ + # override version number + $tempFileName = $spec + "_temp.nuspec" + $originalFileName = $spec + ".nuspec" + + Copy-Item $originalFileName $tempFileName + $tempFile = gi $tempFileName + + [xml] $spec = gc $tempFileName + $spec.package.metadata.version = $thisVersion + Write-Host $tempFile + $spec.Save($tempFile.FullName) + + # run packaging + .\NuGet.exe pack $tempFile.FullName -Verbosity normal + + # remove temp nuspec + ri $tempFile.FullName + } +} \ No newline at end of file diff --git a/tools/psake/README.markdown b/tools/psake/README.markdown new file mode 100644 index 00000000..99c8ddfb --- /dev/null +++ b/tools/psake/README.markdown @@ -0,0 +1,58 @@ +Welcome to the psake project. +============================= + +psake is a build automation tool written in PowerShell. It avoids the angle-bracket tax associated with executable XML by leveraging the PowerShell syntax in your build scripts. +psake has a syntax inspired by rake (aka make in Ruby) and bake (aka make in Boo), but is easier to script because it leverages your existent command-line knowledge. + +psake is pronounced sake – as in Japanese rice wine. It does NOT rhyme with make, bake, or rake. + +## How to get started: + +**Step 1:** Download and extract the project + +You will need to "unblock" the zip file before extracting - PowerShell by default does not run files downloaded from the internet. +Just right-click the zip and click on "properties" and click on the "unblock" button. + +**Step 2:** CD into the directory where you extracted the project (where the psake.psm1 file is) + +> Import-Module .\psake.psm1 + +If you encounter the following error "Import-Module : ...psake.psm1 cannot be loaded because the execution of scripts is disabled on this system. Please see "get-help about_signing" for more details. + +1. Run PowerShell as administrator +2. Set-ExecutionPolicy RemoteSigned + +> Get-Help Invoke-psake -Full +> - this will show you help and examples of how to use psake + +**Step 3:** Run some examples + +> CD .\examples +> +> Invoke-psake +> - This will execute the "default" task in the "default.ps1" +> +> Invoke-psake .\default.ps1 Clean +> - will execute the single task in the default.ps1 script + +## How To Contribute, Collaborate, Communicate + +If you'd like to get involved with psake, we have discussion groups over at google: **[psake-dev](http://groups.google.com/group/psake-dev)** **[psake-users](http://groups.google.com/group/psake-users)** + +Anyone can fork the main repository and submit patches, as well. And lastly, the [wiki](http://wiki.github.com/psake/psake/) and [issues list](http://github.com/psake/psake/issues) are also open for additions, edits, and discussion. + +Also check out the **[psake-contrib](http://github.com/psake/psake-contrib)** project for scripts,modules and functions to help you with a build + +## Contributors + +Many thanks for contributions to psake are due (in alphabetical order): + +* candland +* Staxmanade +* lanwin +* smbecker +* stej + +## License + +psake is released under the [MIT license](http://www.opensource.org/licenses/MIT). diff --git a/tools/psake/chocolateyInstall.ps1 b/tools/psake/chocolateyInstall.ps1 new file mode 100644 index 00000000..88c22ad6 --- /dev/null +++ b/tools/psake/chocolateyInstall.ps1 @@ -0,0 +1,19 @@ +try { + $nugetPath = $env:ChocolateyInstall + $nugetExePath = Join-Path $nuGetPath 'bin' + $packageBatchFileName = Join-Path $nugetExePath "psake.bat" + + $psakeDir = (Split-Path -parent $MyInvocation.MyCommand.Definition) + #$path = ($psakeDir | Split-Path | Join-Path -ChildPath 'psake.cmd') + $path = Join-Path $psakeDir 'psake.cmd' + Write-Host "Adding $packageBatchFileName and pointing to $path" + "@echo off + ""$path"" %*" | Out-File $packageBatchFileName -encoding ASCII + + write-host "PSake is now ready. You can type 'psake' from any command line at any path. Get started by typing 'psake /?'" + + Write-ChocolateySuccess 'psake' +} catch { + Write-ChocolateyFailure 'psake' "$($_.Exception.Message)" + throw +} \ No newline at end of file diff --git a/tools/psake/en-US/psake.psm1-help.xml b/tools/psake/en-US/psake.psm1-help.xml new file mode 100644 index 00000000..b2c61406 --- /dev/null +++ b/tools/psake/en-US/psake.psm1-help.xml @@ -0,0 +1,2130 @@ + + + + + Invoke-psake + + Runs a psake build script. + + + + + Invoke + psake + + + + This function runs a psake build script + + + + Invoke-psake + + buildFile + String + + + taskList + String[] + + + framework + String + + + docs + SwitchParameter + + + parameters + Hashtable + + + properties + Hashtable + + + nologo + SwitchParameter + + + + + + buildFile + + The path to the psake build script to execute + + String + + String + + + 'default.ps1' + + + taskList + + A comma-separated list of task names to execute + + String[] + + String[] + + + + + + framework + + The version of the .NET framework you want to use during build. You can append x86 or x64 to force a specific framework. If not specified, x86 or x64 will be detected based on the bitness of the PowerShell process. +Possible values: '1.0', '1.1', '2.0', '2.0x86', '2.0x64', '3.0', '3.0x86', '3.0x64', '3.5', '3.5x86', '3.5x64', '4.0', '4.0x86', '4.0x64', '4.5', '4.5x86', '4.5x64', '4.5.1', '4.5.1x86', '4.5.1x64' + + String + + String + + + '3.5' + + + docs + + Prints a list of tasks and their descriptions + + SwitchParameter + + SwitchParameter + + + + + + parameters + + A hashtable containing parameters to be passed into the current build script. These parameters will be processed before the 'Properties' function of the script is processed. This means you can access parameters from within the 'Properties' function! + + + Hashtable + + Hashtable + + + + + + properties + + A hashtable containing properties to be passed into the current build script. These properties will override matching properties that are found in the 'Properties' function of the script. + + Hashtable + + Hashtable + + + + + + nologo + + Do not display the startup banner and copyright message. + + SwitchParameter + + SwitchParameter + + + + + + + + + None + + + + + + + + + + + + None + + + + + + + + + + + + + + ---- Exceptions ---- + If there is an exception thrown during the running of a build script psake will set the '$psake.build_success' variable to $false. To detect failue outside PowerShell (for example by build server), finish PowerShell process with non-zero exit code when '$psake.build_success' is $false. Calling psake from 'cmd.exe' with 'psake.cmd' will give you that behaviour. + + + + ---- $psake variable ---- + When the psake module is loaded a variable called $psake is created which is a hashtable +containing some variables: + +$psake.version # contains the current version of psake +$psake.context # holds onto the current state of all variables +$psake.run_by_psake_build_tester # indicates that build is being run by psake-BuildTester +$psake.config_default # contains default configuration + # can be overriden in psake-config.ps1 in directory with psake.psm1 or in directory with current build script +$psake.build_success # indicates that the current build was successful +$psake.build_script_file # contains a System.IO.FileInfo for the current build script +$psake.build_script_dir # contains the fully qualified path to the current build script + +You should see the following when you display the contents of the $psake variable right after importing psake + +PS projects:\psake> Import-Module .\psake.psm1 +PS projects:\psake> $psake + +Name Value +---- ----- +run_by_psake_build_tester False +version 4.2 +build_success False +build_script_file +build_script_dir +config_default @{framework=3.5; ... +context {} + +After a build is executed the following $psake values are updated: build_script_file, build_script_dir, build_success + +PS projects:\psake> Invoke-psake .\examples\default.ps1 +Executing task: Clean +Executed Clean! +Executing task: Compile +Executed Compile! +Executing task: Test +Executed Test! + +Build Succeeded! + +---------------------------------------------------------------------- +Build Time Report +---------------------------------------------------------------------- +Name Duration +---- -------- +Clean 00:00:00.0798486 +Compile 00:00:00.0869948 +Test 00:00:00.0958225 +Total: 00:00:00.2712414 + +PS projects:\psake> $psake + +Name Value +---- ----- +build_script_file YOUR_PATH\examples\default.ps1 +run_by_psake_build_tester False +build_script_dir YOUR_PATH\examples +context {} +version 4.2 +build_success True +config_default @{framework=3.5; ... + + + + + + + + -------------- EXAMPLE 1 -------------- + + C:\PS> + + Invoke-psake + + Description + ----------- + Runs the 'default' task in the '.build.ps1' build script + + + + + + + + + + + + -------------- EXAMPLE 2 -------------- + + C:\PS> + + Invoke-psake '.\build.ps1' Tests,Package + + Description + ----------- + Runs the 'Tests' and 'Package' tasks in the '.build.ps1' build script + + + + + + + + + + + + -------------- EXAMPLE 3 -------------- + + C:\PS> + + Invoke-psake Tests + + Description + ----------- + This example will run the 'Tests' tasks in the 'default.ps1' build script. The 'default.ps1' is assumed to be in the current directory. + + + + + + + + + + + + -------------- EXAMPLE 4 -------------- + + C:\PS> + + Invoke-psake 'Tests, Package' + + Description + ----------- + This example will run the 'Tests' and 'Package' tasks in the 'default.ps1' build script. The 'default.ps1' is assumed to be in the current directory. + NOTE: The quotes around the list of tasks to execute is required if you want to execute more than 1 task. + + + + + + + + + + + -------------- EXAMPLE 5 -------------- + + C:\PS> + + Invoke-psake .\build.ps1 -docs + + Description + ----------- + Prints a report of all the tasks and their dependencies and descriptions and then exits + + + + + + + + + + + + -------------- EXAMPLE 6 -------------- + + C:\PS> + + Invoke-psake .\parameters.ps1 -parameters @{"p1"="v1";"p2"="v2"} + + Description + ----------- + Runs the build script called 'parameters.ps1' and passes in parameters 'p1' and 'p2' with values 'v1' and 'v2' + Here's the .\parameters.ps1 build script: + + + properties { + $my_property = $p1 + $p2 +} + +task default -depends TestParams + +task TestParams { + Assert ($my_property -ne $null) '$my_property should not be null' +} + + Notice how you can refer to the parameters that were passed into the script from within the “properties” function. The value of the $p1 variable should be the string “v1” and the value of the $p2 variable should be “v2”. + + + + + + + + + + -------------- EXAMPLE 7 -------------- + + C:\PS> + + Invoke-psake .\properties.ps1 -properties @{"x"="1";"y"="2"} + + Description + ----------- + Runs the build script called 'properties.ps1' and passes in parameters 'x' and 'y' with values '1' and '2' + This feature allows you to over-ride existing properties in your build script. + + Here's the .\properties.ps1 build script + properties { + $x = $null + $y = $null + $z = $null +} + +task default -depends TestProperties + +task TestProperties { + Assert ($x -ne $null) "x should not be null" + Assert ($y -ne $null) "y should not be null" + Assert ($z -eq $null) "z should be null" +} + + + + + + + + + + + + + Task + + + + Include + + + + Properties + + + + FormatTaskName + + + + TaskSetup + + + + TaskTearDown + + + + Assert + + + + Framework + + + + + + + + TaskTearDown + + Adds a scriptblock to the build that will be executed after each task + + + + + + + + + + This function will accept a scriptblock that will be executed after each +task in the build script. + + + + TaskTearDown + + teardown + ScriptBlock + + + + + + teardown + + A scriptblock to execute + + ScriptBlock + + ScriptBlock + + + + + + + + + None + + + + + + + + + + + + None + + + + + + + + + + + + + + + + + + + -------------- EXAMPLE 1 -------------- + + C:\PS> + + A sample build script is shown below: + +Task default -depends Test + +Task Test -depends Compile, Clean { +} + +Task Compile -depends Clean { +} + +Task Clean { +} + +TaskTearDown { + "Running 'TaskTearDown' for task $context.Peek().currentTaskName" +} + + Description + ----------- + The script above produces the following output: + Executing task, Clean... +Running 'TaskTearDown' for task Clean +Executing task, Compile... +Running 'TaskTearDown' for task Compile +Executing task, Test... +Running 'TaskTearDown' for task Test + +Build Succeeded + + + + + + + + + + + + Task + + + + Include + + + + Properties + + + + Invoke-psake + + + + TaskSetup + + + + TaskTearDown + + + + Assert + + + + Framework + + + + + + + + TaskSetup + + Adds a scriptblock that will be executed before each task + + + + + + + + + + This function will accept a scriptblock that will be executed before each +task in the build script. + + + + TaskSetup + + setup + ScriptBlock + + + + + + setup + + A scriptblock to execute + + ScriptBlock + + ScriptBlock + + + + + + + + + None + + + + + + + + + + + + None + + + + + + + + + + + + + + + + + + + -------------- EXAMPLE 1 -------------- + + C:\PS> + + A sample build script is shown below: + +Task default -depends Test + +Task Test -depends Compile, Clean { +} + +Task Compile -depends Clean { +} + +Task Clean { +} + +TaskSetup { + "Running 'TaskSetup' for task $context.Peek().currentTaskName" +} + + Description + ----------- + The script above produces the following output: + Running 'TaskSetup' for task Clean +Executing task, Clean... +Running 'TaskSetup' for task Compile +Executing task, Compile... +Running 'TaskSetup' for task Test +Executing task, Test... + +Build Succeeded + + + + + + + + + + + + Task + + + + Include + + + + Properties + + + + Invoke-psake + + + + FormatTaskName + + + + TaskTearDown + + + + Assert + + + + Framework + + + + + + + + Framework + + Sets the version of the .NET framework you want to use during build. + + + + + + + + + + This function will accept a string containing version of the .NET framework to use during build. Possible values: '1.0', '1.1', '2.0', '2.0x86', '2.0x64', '3.0', '3.0x86', '3.0x64', '3.5', '3.5x86', '3.5x64', '4.0', '4.0x86', '4.0x64', '4.5', '4.5x86', '4.5x64', '4.5.1', '4.5.1x86', '4.5.1x64'. Default is '3.5*', where x86 or x64 will be detected based on the bitness of the PowerShell process. + + + + Framework + + framework + string + + + + + + framework + + Version of the .NET framework to use during build. + + string + + string + + + + + + + + + None + + + + + + + + + + + + None + + + + + + + + + + + + + + + + + + + -------------- EXAMPLE 1 -------------- + +Framework "4.0" + +Task default -depends Compile + +Task Compile -depends Clean { + msbuild /version +} + + Description + ----------- + The script above will output detailed version of msbuid v4 + + + + + + + + + + + + Task + + + + Include + + + + Properties + + + + Invoke-psake + + + + FormatTaskName + + + + TaskSetup + + + + TaskTearDown + + + + Assert + + + + + + + + FormatTaskName + + This function allows you to change how psake renders the task name during a build. + + + + + + + + + + This function takes either a string which represents a format string (formats using the -f format operator see “help about_operators”) or it can accept a script block that has a single parameter that is the name of the task that will be executed. + + + + FormatTaskName + + format + String or ScriptBlock + + + + + + format + + A format string or a scriptblock to execute + + String or ScriptBlock + + String or ScriptBlock + + + + + + + + + None + + + + + + + + + + + + None + + + + + + + + + + + + + + + + + + + -------------- EXAMPLE 1 -------------- + + C:\PS> + + A sample build script that uses a format string is shown below: + +Task default -depends TaskA, TaskB, TaskC + +FormatTaskName "-------- {0} --------" + +Task TaskA { + "TaskA is executing" +} + +Task TaskB { + "TaskB is executing" +} + +Task TaskC { + "TaskC is executing" +} + + Description + ----------- + The script above produces the following output: + -------- TaskA -------- +TaskA is executing +-------- TaskB -------- +TaskB is executing +-------- TaskC -------- +TaskC is executing + +Build Succeeded! + + + + + + + + + + -------------- EXAMPLE 2 -------------- + + C:\PS> + + A sample build script that uses a ScriptBlock is shown below: + +Task default -depends TaskA, TaskB, TaskC + +FormatTaskName { + param($taskName) + write-host "Executing Task: $taskName" -foregroundcolor blue +} + +Task TaskA { + "TaskA is executing" +} + +Task TaskB { + "TaskB is executing" +} + +Task TaskC { + "TaskC is executing" +} + + Description + ----------- + The preceding example uses the scriptblock parameter to the FormatTaskName function to render each task name in the color blue. + Note: the $taskName parameter is arbitrary it could be named anything + + + + + + + + + + + + Task + + + + Include + + + + Properties + + + + Invoke-psake + + + + TaskSetup + + + + TaskTearDown + + + + Assert + + + + Framework + + + + + + + + Include + + Include the functions or code of another powershell script file into the current build script's scope + + + + + + + + + + A build script may declare an "includes" function which allows you to define a file containing powershell code to be included and added to the scope of the currently running build script. Code from such file will be executed after code from build script. + + + + Include + + fileNamePathToInclude + String + + + + + + fileNamePathToInclude + + A string containing the path and name of the powershell file to include + + String + + String + + + + + + + + + None + + + + + + + + + + + + None + + + + + + + + + + + + + + + + + + + -------------- EXAMPLE 1 -------------- + + C:\PS> + + A sample build script is shown below: + +Include ".\build_utils.ps1" + +Task default -depends Test + +Task Test -depends Compile, Clean { +} + +Task Compile -depends Clean { +} + +Task Clean { +} + + Description + ----------- + The script above includes all the functions and variables defined in the ".\build_utils.ps1" script into the current build script's scope + Note: You can have more than 1 "Include" function defined in the build script + + + + + + + + + + + + Task + + + + FormatTaskName + + + + Properties + + + + Invoke-psake + + + + TaskSetup + + + + TaskTearDown + + + + Assert + + + + Framework + + + + + + + + Properties + + Define a scriptblock that contains assignments to variables that will be available to all tasks in the build script + + + + + + + + + + A build script may declare a "Properies" function which allows you to define variables that will be available to all the "Task" functions in the build script. + + + + Properties + + properties + ScriptBlock + + + + + + properties + + The script block containing all the variable assignment statements + + ScriptBlock + + ScriptBlock + + + + + + + + + None + + + + + + + + + + + + None + + + + + + + + + + + + + + + + + + + -------------- EXAMPLE 1 -------------- + + C:\PS> + + A sample build script is shown below: + +Properties { + $build_dir = "c:\build" + $connection_string = "datasource=localhost;initial catalog=northwind;integrated security=sspi" +} + +Task default -depends Test + +Task Test -depends Compile, Clean { +} + +Task Compile -depends Clean { +} + +Task Clean { +} + + Description + ----------- + Note: You can have more than 1 "Properties" function defined in the build script + + + + + + + + + + + + + + + Task + + + + FormatTaskName + + + + Include + + + + Invoke-psake + + + + TaskSetup + + + + TaskTearDown + + + + Assert + + + + Framework + + + + + + + + Task + + Defines a build task to be executed by psake + + + + + + + + + + This function creates a 'task' object that will be used by the psake engine to execute a build task. Note: There must be at least one task called 'default' in the build script + + + + Task + + Name + String + + + Action + ScriptBlock + + + PreAction + ScriptBlock + + + PostAction + ScriptBlock + + + Precondition + ScriptBlock + + + Postcondition + ScriptBlock + + + ContinueOnError + Boolean + + + Depends + String[] + + + RequiredVariables + String[] + + + Description + String[] + + + + + + Name + + The name of the task + + String + + String + + + + + + Action + + A scriptblock containing the statements to execute for the task. + + ScriptBlock + + ScriptBlock + + + + + + PreAction + + A scriptblock to be executed before the 'Action' scriptblock. Note: This parameter is ignored if the 'Action' scriptblock is not defined. + + ScriptBlock + + ScriptBlock + + + + + + PostAction + + A scriptblock to be executed after the 'Action' scriptblock. Note: This parameter is ignored if the 'Action' scriptblock is not defined. + + ScriptBlock + + ScriptBlock + + + + + + Precondition + + A scriptblock that is executed to determine if the task is executed or skipped. This scriptblock should return $true or $false + + ScriptBlock + + ScriptBlock + + + + + + Postcondition + + A scriptblock that is executed to determine if the task completed its job correctly. An exception is thrown if the scriptblock returns $false. + + ScriptBlock + + ScriptBlock + + + + + + ContinueOnError + + If this switch parameter is set then the task will not cause the build to fail when an exception is thrown by the task + + SwitchParameter + + SwitchParameter + + + + + + Depends + + An array of task names that this task depends on. These tasks will be executed before the current task is executed. + + String[] + + String[] + + + + + + RequiredVariables + + An array of names of variables that must be set to run this task. + + String[] + + String[] + + + + + + Description + + A description of the task. + + String + + String + + + + + + + + + None + + + + + + + + + + + + None + + + + + + + + + + + + + + + + + + + -------------- EXAMPLE 1 -------------- + + C:\PS> + + A sample build script is shown below: + +Task default -Depends Test + +Task Test -Depends Compile, Clean { + "This is a test" +} + +Task Compile -Depends Clean { + "Compile" +} + +Task Clean { + "Clean" +} + +The 'default' task is required and should not contain an 'Action' parameter. +It uses the 'Depends' parameter to specify that 'Test' is a dependency + +The 'Test' task uses the 'Depends' parameter to specify that 'Compile' and 'Clean' are dependencies +The 'Compile' task depends on the 'Clean' task. + +Note: +The 'Action' parameter is defaulted to the script block following the 'Clean' task. + +An equivalent 'Test' task is shown below: + +Task Test -Depends Compile, Clean -Action { + $testMessage +} + +The output for the above sample build script is shown below: +Executing task, Clean... +Clean +Executing task, Compile... +Compile +Executing task, Test... +This is a test + +Build Succeeded! + +---------------------------------------------------------------------- +Build Time Report +---------------------------------------------------------------------- +Name Duration +---- -------- +Clean 00:00:00.0065614 +Compile 00:00:00.0133268 +Test 00:00:00.0225964 +Total: 00:00:00.0782496 + + + + + + + + + + + + + + + + Properties + + + + FormatTaskName + + + + Include + + + + Invoke-psake + + + + TaskSetup + + + + TaskTearDown + + + + Assert + + + + Framework + + + + + + + + Assert + + Helper function for "Design by Contract" assertion checking. + + + + + + + + + + This is a helper function that makes the code less noisy by eliminating many of the "if" statements that are normally required to verify assumptions in the code. + + + + Assert + + conditionToCheck + Boolean + + + failureMessage + String + + + + + + conditionToCheck + + The boolean condition to evaluate + + Boolean + + Boolean + + + + + + failureMessage + + The error message used for the exception if the conditionToCheck parameter is false + + String + + String + + + + + + + + + None + + + + + + + + + + + + None + + + + + + + + + + + + + + + + + + + -------------- EXAMPLE 1 -------------- + + C:\PS> + + Assert $false "This always throws an exception" + + + + + + + + + + + + + -------------- EXAMPLE 2 -------------- + + C:\PS> + + Assert ( ($i % 2) -eq 0 ) "$i is not an even number" + + Description + ----------- + This exmaple may throw an exception if $i is not an even number + + Note: + It might be necessary to wrap the condition with paranthesis to force PS to evaluate the condition +so that a boolean value is calculated and passed into the 'conditionToCheck' parameter. + +Example: + Assert 1 -eq 2 "1 doesn't equal 2" + +PS will pass 1 into the condtionToCheck variable and PS will look for a parameter called "eq" and +throw an exception with the following message "A parameter cannot be found that matches parameter name 'eq'" + +The solution is to wrap the condition in () so that PS will evaluate it first. + + Assert (1 -eq 2) "1 doesn't equal 2" + + + + + + + + + + + + + Task + + + + FormatTaskName + + + + Include + + + + Invoke-psake + + + + TaskSetup + + + + TaskTearDown + + + + Properties + + + + + Framework + + + + + + + Exec + + Helper function for executing command-line programs. + + + + + + + + + + This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode to see if an error occcured. If an error is detected then an exception is thrown. This function allows you to run command-line programs without having to explicitly check fthe $lastexitcode variable. + + + + Exec + + cmd + ScriptBlock + + + errorMessage + String + + + + + + cmd + + The scriptblock to execute. This scriptblock will typically contain the command-line invocation. + + ScriptBlock + + Boolean + + + + + + failureMessage + + The error message used for the exception that is thrown. + + String + + String + + + + + + + + + None + + + + + + + + + + + + None + + + + + + + + + + + + + + + + + + + -------------- EXAMPLE 1 -------------- + + C:\PS> + + exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed" + + This example calls the svn command-line client. + + + + + + + + + + + + + + Assert + + + + FormatTaskName + + + + Include + + + + Invoke-psake + + + + Properties + + + + Task + + + + TaskSetup + + + + TaskTearDown + + + + + Framework + + + + + + + Invoke-Task + + Executes another task in the current build script. + + + + + + + + + + This is a function that will allow you to invoke a Task from within another Task in the current build script. + + + + Invoke-Task + + taskName + String + + + + + + taskName + + The name of the task to execute. + + String + + String + + + + + + + + + None + + + + + + + + + + + + None + + + + + + + + + + + + + + + + + + + -------------- EXAMPLE 1 -------------- + + C:\PS> + + Invoke-Task "Compile" + + This example calls the "Compile" task. + + + + + + + + + + + + + + Assert + + + + Exec + + + + FormatTaskName + + + + Include + + + + Invoke-psake + + + + Properties + + + + Task + + + + TaskSetup + + + + TaskTearDown + + + + Framework + + + + + diff --git a/tools/psake/examples/checkvariables.ps1 b/tools/psake/examples/checkvariables.ps1 new file mode 100644 index 00000000..0d6b0b12 --- /dev/null +++ b/tools/psake/examples/checkvariables.ps1 @@ -0,0 +1,33 @@ +Properties { + $x = 1 + $y = 2 +} + +FormatTaskName "[{0}]" + +Task default -Depends Verify + +Task Verify -Description "This task verifies psake's variables" { + + Assert (Test-Path 'variable:\psake') "psake variable was not exported from module" + + Assert ($psake.ContainsKey("version")) "psake variable does not contain 'version'" + Assert ($psake.ContainsKey("context")) "psake variable does not contain 'context'" + Assert ($psake.ContainsKey("build_success")) "psake variable does not contain 'build_success'" + Assert ($psake.ContainsKey("build_script_file")) "psake variable does not contain 'build_script_file'" + Assert ($psake.ContainsKey("build_script_dir")) "psake variable does not contain 'build_script_dir'" + + Assert (![string]::IsNullOrEmpty($psake.version)) '$psake.version was null or empty' + Assert ($psake.context -ne $null) '$psake.context was null' + Assert (!$psake.build_success) '$psake.build_success should be $false' + Assert ($psake.build_script_file -ne $null) '$psake.build_script_file was null' + Assert ($psake.build_script_file.Name -eq "checkvariables.ps1") ("psake variable: {0} was not equal to 'checkvariables.ps1'" -f $psake.build_script_file.Name) + Assert (![string]::IsNullOrEmpty($psake.build_script_dir)) '$psake.build_script_dir was null or empty' + + Assert ($psake.context.Peek().tasks.Count -ne 0) "psake context variable 'tasks' had length zero" + Assert ($psake.context.Peek().properties.Count -ne 0) "psake context variable 'properties' had length zero" + Assert ($psake.context.Peek().includes.Count -eq 0) "psake context variable 'includes' should have had length zero" + Assert ($psake.context.Peek().config -ne $null) "psake context variable 'config' was null" + + Assert ($psake.context.Peek().currentTaskName -eq "Verify") 'psake variable: $currentTaskName was not set correctly' +} \ No newline at end of file diff --git a/tools/psake/examples/continueonerror.ps1 b/tools/psake/examples/continueonerror.ps1 new file mode 100644 index 00000000..c8a633f7 --- /dev/null +++ b/tools/psake/examples/continueonerror.ps1 @@ -0,0 +1,14 @@ +Task default -Depends TaskA + +Task TaskA -Depends TaskB { + "Task - A" +} + +Task TaskB -Depends TaskC -ContinueOnError { + "Task - B" + throw "I failed on purpose!" +} + +Task TaskC { + "Task - C" +} \ No newline at end of file diff --git a/tools/psake/examples/default.ps1 b/tools/psake/examples/default.ps1 new file mode 100644 index 00000000..4aca3586 --- /dev/null +++ b/tools/psake/examples/default.ps1 @@ -0,0 +1,23 @@ +properties { + $testMessage = 'Executed Test!' + $compileMessage = 'Executed Compile!' + $cleanMessage = 'Executed Clean!' +} + +task default -depends Test + +task Test -depends Compile, Clean { + $testMessage +} + +task Compile -depends Clean { + $compileMessage +} + +task Clean { + $cleanMessage +} + +task ? -Description "Helper to display task info" { + Write-Documentation +} \ No newline at end of file diff --git a/tools/psake/examples/formattaskname_scriptblock.ps1 b/tools/psake/examples/formattaskname_scriptblock.ps1 new file mode 100644 index 00000000..e4919b97 --- /dev/null +++ b/tools/psake/examples/formattaskname_scriptblock.ps1 @@ -0,0 +1,24 @@ +properties { + $testMessage = 'Executed Test!' + $compileMessage = 'Executed Compile!' + $cleanMessage = 'Executed Clean!' +} + +task default -depends Test + +formatTaskName { + param($taskName) + write-host $taskName -foregroundcolor Green +} + +task Test -depends Compile, Clean { + $testMessage +} + +task Compile -depends Clean { + $compileMessage +} + +task Clean { + $cleanMessage +} \ No newline at end of file diff --git a/tools/psake/examples/formattaskname_string.ps1 b/tools/psake/examples/formattaskname_string.ps1 new file mode 100644 index 00000000..66874900 --- /dev/null +++ b/tools/psake/examples/formattaskname_string.ps1 @@ -0,0 +1,21 @@ +properties { + $testMessage = 'Executed Test!' + $compileMessage = 'Executed Compile!' + $cleanMessage = 'Executed Clean!' +} + +task default -depends Test + +formatTaskName "-------{0}-------" + +task Test -depends Compile, Clean { + $testMessage +} + +task Compile -depends Clean { + $compileMessage +} + +task Clean { + $cleanMessage +} \ No newline at end of file diff --git a/tools/psake/examples/msbuild40.ps1 b/tools/psake/examples/msbuild40.ps1 new file mode 100644 index 00000000..2298e42c --- /dev/null +++ b/tools/psake/examples/msbuild40.ps1 @@ -0,0 +1,8 @@ +Framework "4.0" +# Framework "4.0x64" + +task default -depends ShowMsBuildVersion + +task ShowMsBuildVersion { + msbuild /version +} \ No newline at end of file diff --git a/tools/psake/examples/nested.ps1 b/tools/psake/examples/nested.ps1 new file mode 100644 index 00000000..36fc830e --- /dev/null +++ b/tools/psake/examples/nested.ps1 @@ -0,0 +1,17 @@ +Properties { + $x = 1 +} + +Task default -Depends RunNested1, RunNested2, CheckX + +Task RunNested1 { + Invoke-psake .\nested\nested1.ps1 +} + +Task RunNested2 { + Invoke-psake .\nested\nested2.ps1 +} + +Task CheckX{ + Assert ($x -eq 1) '$x was not 1' +} \ No newline at end of file diff --git a/tools/psake/examples/nested/nested1.ps1 b/tools/psake/examples/nested/nested1.ps1 new file mode 100644 index 00000000..81790ec9 --- /dev/null +++ b/tools/psake/examples/nested/nested1.ps1 @@ -0,0 +1 @@ +Properties { $x = 100 } Task default -Depends Nested1CheckX Task Nested1CheckX{ Assert ($x -eq 100) '$x was not 100' } \ No newline at end of file diff --git a/tools/psake/examples/nested/nested2.ps1 b/tools/psake/examples/nested/nested2.ps1 new file mode 100644 index 00000000..7a4a2b8c --- /dev/null +++ b/tools/psake/examples/nested/nested2.ps1 @@ -0,0 +1 @@ +Properties { $x = 200 } Task default -Depends Nested2CheckX Task Nested2CheckX{ Assert ($x -eq 200) '$x was not 200' } \ No newline at end of file diff --git a/tools/psake/examples/parameters.ps1 b/tools/psake/examples/parameters.ps1 new file mode 100644 index 00000000..0a902b72 --- /dev/null +++ b/tools/psake/examples/parameters.ps1 @@ -0,0 +1,9 @@ +properties { + $my_property = $p1 + $p2 +} + +task default -depends TestParams + +task TestParams { + Assert ($my_property -ne $null) "`$my_property should not be null. Run with -parameters @{'p1' = 'v1'; 'p2' = 'v2'}" +} \ No newline at end of file diff --git a/tools/psake/examples/passingParametersString/build.Release.Version.bat b/tools/psake/examples/passingParametersString/build.Release.Version.bat new file mode 100644 index 00000000..a48c1269 --- /dev/null +++ b/tools/psake/examples/passingParametersString/build.Release.Version.bat @@ -0,0 +1,3 @@ +powershell -Command "& {Import-Module .\..\..\psake.psm1; Invoke-psake .\parameters.ps1 -parameters @{"buildConfiguration"='Release';} }" + +Pause \ No newline at end of file diff --git a/tools/psake/examples/passingParametersString/parameters.ps1 b/tools/psake/examples/passingParametersString/parameters.ps1 new file mode 100644 index 00000000..48509759 --- /dev/null +++ b/tools/psake/examples/passingParametersString/parameters.ps1 @@ -0,0 +1,22 @@ +properties { + $buildOutputPath = ".\bin\$buildConfiguration" +} + +task default -depends DoRelease + +task DoRelease { + Assert ("$buildConfiguration" -ne $null) "buildConfiguration should not have been null" + Assert ("$buildConfiguration" -eq 'Release') "buildConfiguration=[$buildConfiguration] should have been 'Release'" + + Write-Host "" + Write-Host "" + Write-Host "" + Write-Host -NoNewline "Would build output into path " + Write-Host -NoNewline -ForegroundColor Green "$buildOutputPath" + Write-Host -NoNewline " for build configuration " + Write-Host -ForegroundColor Green "$buildConfiguration" + Write-Host -NoNewline "." + Write-Host "" + Write-Host "" + Write-Host "" +} diff --git a/tools/psake/examples/preandpostaction.ps1 b/tools/psake/examples/preandpostaction.ps1 new file mode 100644 index 00000000..fcb36e6f --- /dev/null +++ b/tools/psake/examples/preandpostaction.ps1 @@ -0,0 +1,13 @@ +task default -depends Test + +task Test -depends Compile, Clean -PreAction {"Pre-Test"} -Action { + "Test" +} -PostAction {"Post-Test"} + +task Compile -depends Clean { + "Compile" +} + +task Clean { + "Clean" +} \ No newline at end of file diff --git a/tools/psake/examples/preandpostcondition.ps1 b/tools/psake/examples/preandpostcondition.ps1 new file mode 100644 index 00000000..ce855fb3 --- /dev/null +++ b/tools/psake/examples/preandpostcondition.ps1 @@ -0,0 +1,18 @@ +properties { + $runTaskA = $false + $taskBSucceded = $true +} + +task default -depends TaskC + +task TaskA -precondition { $runTaskA -eq $true } { + "TaskA executed" +} + +task TaskB -postcondition { $taskBSucceded -eq $true } { + "TaskB executed" +} + +task TaskC -depends TaskA,TaskB { + "TaskC executed." +} \ No newline at end of file diff --git a/tools/psake/examples/properties.ps1 b/tools/psake/examples/properties.ps1 new file mode 100644 index 00000000..ccbb3ec8 --- /dev/null +++ b/tools/psake/examples/properties.ps1 @@ -0,0 +1,13 @@ +properties { + $x = $null + $y = $null + $z = $null +} + +task default -depends TestProperties + +task TestProperties { + Assert ($x -ne $null) "x should not be null. Run with -properties @{'x' = '1'; 'y' = '2'}" + Assert ($y -ne $null) "y should not be null. Run with -properties @{'x' = '1'; 'y' = '2'}" + Assert ($z -eq $null) "z should be null" +} \ No newline at end of file diff --git a/tools/psake/examples/requiredvariables.ps1 b/tools/psake/examples/requiredvariables.ps1 new file mode 100644 index 00000000..d9922bb0 --- /dev/null +++ b/tools/psake/examples/requiredvariables.ps1 @@ -0,0 +1,14 @@ +properties { + $x = $null + $y = $null + $z = $null +} + +task default -depends TestRequiredVariables + +# you can put arguments to task in multiple lines using ` +task TestRequiredVariables ` + -description "This task shows how to make a variable required to run task. Run this script with -properties @{x = 1; y = 2; z = 3}" ` + -requiredVariables x, y, z ` +{ +} diff --git a/tools/psake/examples/tasksetupandteardown.ps1 b/tools/psake/examples/tasksetupandteardown.ps1 new file mode 100644 index 00000000..be0834af --- /dev/null +++ b/tools/psake/examples/tasksetupandteardown.ps1 @@ -0,0 +1,17 @@ +TaskSetup { + "Executing task setup" +} + +TaskTearDown { + "Executing task tear down" +} + +Task default -depends TaskB + +Task TaskA { + "TaskA executed" +} + +Task TaskB -depends TaskA { + "TaskB executed" +} diff --git a/tools/psake/init.ps1 b/tools/psake/init.ps1 new file mode 100644 index 00000000..77ee7121 --- /dev/null +++ b/tools/psake/init.ps1 @@ -0,0 +1,10 @@ +param($installPath, $toolsPath, $package) + +$psakeModule = Join-Path $toolsPath psake.psm1 +import-module $psakeModule + +@" +======================== +psake - Automated builds with powershell +======================== +"@ | Write-Host \ No newline at end of file diff --git a/tools/psake/license.txt b/tools/psake/license.txt new file mode 100644 index 00000000..1d446c58 --- /dev/null +++ b/tools/psake/license.txt @@ -0,0 +1,20 @@ +psake +Copyright (c) 2012-13 James Kovacs, Damian Hickey and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/tools/psake/psake-config.ps1 b/tools/psake/psake-config.ps1 new file mode 100644 index 00000000..54587016 --- /dev/null +++ b/tools/psake/psake-config.ps1 @@ -0,0 +1,21 @@ +<# +------------------------------------------------------------------- +Defaults +------------------------------------------------------------------- +$config.buildFileName="default.ps1" +$config.framework = "4.0" +$config.taskNameFormat="Executing {0}" +$config.verboseError=$false +$config.coloredOutput = $true +$config.modules=$null + +------------------------------------------------------------------- +Load modules from .\modules folder and from file my_module.psm1 +------------------------------------------------------------------- +$config.modules=(".\modules\*.psm1",".\my_module.psm1") + +------------------------------------------------------------------- +Use scriptblock for taskNameFormat +------------------------------------------------------------------- +$config.taskNameFormat= { param($taskName) "Executing $taskName at $(get-date)" } +#> diff --git a/tools/psake/psake.cmd b/tools/psake/psake.cmd new file mode 100644 index 00000000..f1aadc48 --- /dev/null +++ b/tools/psake/psake.cmd @@ -0,0 +1,14 @@ +@echo off +rem Helper script for those who want to run psake from cmd.exe +rem Example run from cmd.exe: +rem psake "default.ps1" "BuildHelloWord" "4.0" + +if '%1'=='/?' goto help +if '%1'=='-help' goto help +if '%1'=='-h' goto help + +powershell -NoProfile -ExecutionPolicy Bypass -Command "& '%~dp0\psake.ps1' %*; if ($psake.build_success -eq $false) { exit 1 } else { exit 0 }" +exit /B %errorlevel% + +:help +powershell -NoProfile -ExecutionPolicy Bypass -Command "& '%~dp0\psake.ps1' -help" diff --git a/tools/psake/psake.ps1 b/tools/psake/psake.ps1 new file mode 100644 index 00000000..7c6bdcd6 --- /dev/null +++ b/tools/psake/psake.ps1 @@ -0,0 +1,51 @@ +# Helper script for those who want to run psake without importing the module. +# Example run from PowerShell: +# .\psake.ps1 "default.ps1" "BuildHelloWord" "4.0" + +# Must match parameter definitions for psake.psm1/invoke-psake +# otherwise named parameter binding fails +param( + [Parameter(Position=0,Mandatory=0)] + [string]$buildFile, + [Parameter(Position=1,Mandatory=0)] + [string[]]$taskList = @(), + [Parameter(Position=2,Mandatory=0)] + [string]$framework, + [Parameter(Position=3,Mandatory=0)] + [switch]$docs = $false, + [Parameter(Position=4,Mandatory=0)] + [System.Collections.Hashtable]$parameters = @{}, + [Parameter(Position=5, Mandatory=0)] + [System.Collections.Hashtable]$properties = @{}, + [Parameter(Position=6, Mandatory=0)] + [alias("init")] + [scriptblock]$initialization = {}, + [Parameter(Position=7, Mandatory=0)] + [switch]$nologo = $false, + [Parameter(Position=8, Mandatory=0)] + [switch]$help = $false, + [Parameter(Position=9, Mandatory=0)] + [string]$scriptPath +) + +# setting $scriptPath here, not as default argument, to support calling as "powershell -File psake.ps1" +if (!$scriptPath) { + $scriptPath = $(Split-Path -parent $MyInvocation.MyCommand.path) +} + +# '[p]sake' is the same as 'psake' but $Error is not polluted +remove-module [p]sake +import-module (join-path $scriptPath psake.psm1) +if ($help) { + Get-Help Invoke-psake -full + return +} + +if ($buildPath -and (-not(test-path $buildFile))) { + $absoluteBuildFile = (join-path $scriptPath $buildFile) + if (test-path $absoluteBuildFile) { + $buildFile = $absoluteBuildFile + } +} + +Invoke-psake $buildFile $taskList $framework $docs $parameters $properties $initialization $nologo diff --git a/tools/psake/psake.psm1 b/tools/psake/psake.psm1 new file mode 100644 index 00000000..c0d8ded0 --- /dev/null +++ b/tools/psake/psake.psm1 @@ -0,0 +1,847 @@ +# psake +# Copyright (c) 2012 James Kovacs +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +#Requires -Version 2.0 + +#-- Public Module Functions --# + +# .ExternalHelp psake.psm1-help.xml +function Invoke-Task +{ + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)] [string]$taskName + ) + + Assert $taskName ($msgs.error_invalid_task_name) + + $taskKey = $taskName.ToLower() + + if ($currentContext.aliases.Contains($taskKey)) { + $taskName = $currentContext.aliases.$taskKey.Name + $taskKey = $taskName.ToLower() + } + + $currentContext = $psake.context.Peek() + + Assert ($currentContext.tasks.Contains($taskKey)) ($msgs.error_task_name_does_not_exist -f $taskName) + + if ($currentContext.executedTasks.Contains($taskKey)) { return } + + Assert (!$currentContext.callStack.Contains($taskKey)) ($msgs.error_circular_reference -f $taskName) + + $currentContext.callStack.Push($taskKey) + + $task = $currentContext.tasks.$taskKey + + $precondition_is_valid = & $task.Precondition + + if (!$precondition_is_valid) { + WriteColoredOutput ($msgs.precondition_was_false -f $taskName) -foregroundcolor Cyan + } else { + if ($taskKey -ne 'default') { + + if ($task.PreAction -or $task.PostAction) { + Assert ($task.Action -ne $null) ($msgs.error_missing_action_parameter -f $taskName) + } + + if ($task.Action) { + try { + foreach($childTask in $task.DependsOn) { + Invoke-Task $childTask + } + + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + $currentContext.currentTaskName = $taskName + + & $currentContext.taskSetupScriptBlock + + if ($task.PreAction) { + & $task.PreAction + } + + if ($currentContext.config.taskNameFormat -is [ScriptBlock]) { + & $currentContext.config.taskNameFormat $taskName + } else { + WriteColoredOutput ($currentContext.config.taskNameFormat -f $taskName) -foregroundcolor Cyan + } + + foreach ($variable in $task.requiredVariables) { + Assert ((test-path "variable:$variable") -and ((get-variable $variable).Value -ne $null)) ($msgs.required_variable_not_set -f $variable, $taskName) + } + + & $task.Action + + if ($task.PostAction) { + & $task.PostAction + } + + & $currentContext.taskTearDownScriptBlock + $task.Duration = $stopwatch.Elapsed + } catch { + if ($task.ContinueOnError) { + "-"*70 + WriteColoredOutput ($msgs.continue_on_error -f $taskName,$_) -foregroundcolor Yellow + "-"*70 + $task.Duration = $stopwatch.Elapsed + } else { + throw $_ + } + } + } else { + # no action was specified but we still execute all the dependencies + foreach($childTask in $task.DependsOn) { + Invoke-Task $childTask + } + } + } else { + foreach($childTask in $task.DependsOn) { + Invoke-Task $childTask + } + } + + Assert (& $task.Postcondition) ($msgs.postcondition_failed -f $taskName) + } + + $poppedTaskKey = $currentContext.callStack.Pop() + Assert ($poppedTaskKey -eq $taskKey) ($msgs.error_corrupt_callstack -f $taskKey,$poppedTaskKey) + + $currentContext.executedTasks.Push($taskKey) +} + +# .ExternalHelp psake.psm1-help.xml +function Exec +{ + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd, + [Parameter(Position=1,Mandatory=0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd), + [Parameter(Position=2,Mandatory=0)][int]$maxRetries = 0, + [Parameter(Position=3,Mandatory=0)][string]$retryTriggerErrorPattern = $null + ) + + $tryCount = 1 + + do { + try { + $global:lastexitcode = 0 + & $cmd + if ($lastexitcode -ne 0) { + throw ("Exec: " + $errorMessage) + } + break + } + catch [Exception] + { + if ($tryCount -gt $maxRetries) { + throw $_ + } + + if ($retryTriggerErrorPattern -ne $null) { + $isMatch = [regex]::IsMatch($_.Exception.Message, $retryTriggerErrorPattern) + + if ($isMatch -eq $false) { + throw $_ + } + } + + Write-Host "Try $tryCount failed, retrying again in 1 second..." + + $tryCount++ + + [System.Threading.Thread]::Sleep([System.TimeSpan]::FromSeconds(1)) + } + } + while ($true) +} + +# .ExternalHelp psake.psm1-help.xml +function Assert +{ + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)]$conditionToCheck, + [Parameter(Position=1,Mandatory=1)]$failureMessage + ) + if (!$conditionToCheck) { + throw ("Assert: " + $failureMessage) + } +} + +# .ExternalHelp psake.psm1-help.xml +function Task +{ + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][string]$name = $null, + [Parameter(Position=1,Mandatory=0)][scriptblock]$action = $null, + [Parameter(Position=2,Mandatory=0)][scriptblock]$preaction = $null, + [Parameter(Position=3,Mandatory=0)][scriptblock]$postaction = $null, + [Parameter(Position=4,Mandatory=0)][scriptblock]$precondition = {$true}, + [Parameter(Position=5,Mandatory=0)][scriptblock]$postcondition = {$true}, + [Parameter(Position=6,Mandatory=0)][switch]$continueOnError = $false, + [Parameter(Position=7,Mandatory=0)][string[]]$depends = @(), + [Parameter(Position=8,Mandatory=0)][string[]]$requiredVariables = @(), + [Parameter(Position=9,Mandatory=0)][string]$description = $null, + [Parameter(Position=10,Mandatory=0)][string]$alias = $null, + [Parameter(Position=11,Mandatory=0)][string]$maxRetries = 0, + [Parameter(Position=12,Mandatory=0)][string]$retryTriggerErrorPattern = $null + ) + if ($name -eq 'default') { + Assert (!$action) ($msgs.error_default_task_cannot_have_action) + } + + $newTask = @{ + Name = $name + DependsOn = $depends + PreAction = $preaction + Action = $action + PostAction = $postaction + Precondition = $precondition + Postcondition = $postcondition + ContinueOnError = $continueOnError + Description = $description + Duration = [System.TimeSpan]::Zero + RequiredVariables = $requiredVariables + Alias = $alias + MaxRetries = $maxRetries + RetryTriggerErrorPattern = $retryTriggerErrorPattern + } + + $taskKey = $name.ToLower() + + $currentContext = $psake.context.Peek() + + Assert (!$currentContext.tasks.ContainsKey($taskKey)) ($msgs.error_duplicate_task_name -f $name) + + $currentContext.tasks.$taskKey = $newTask + + if($alias) + { + $aliasKey = $alias.ToLower() + + Assert (!$currentContext.aliases.ContainsKey($aliasKey)) ($msgs.error_duplicate_alias_name -f $alias) + + $currentContext.aliases.$aliasKey = $newTask + } +} + +# .ExternalHelp psake.psm1-help.xml +function Properties { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$properties + ) + $psake.context.Peek().properties += $properties +} + +# .ExternalHelp psake.psm1-help.xml +function Include { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][string]$fileNamePathToInclude + ) + Assert (test-path $fileNamePathToInclude -pathType Leaf) ($msgs.error_invalid_include_path -f $fileNamePathToInclude) + $psake.context.Peek().includes.Enqueue((Resolve-Path $fileNamePathToInclude)); +} + +# .ExternalHelp psake.psm1-help.xml +function FormatTaskName { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)]$format + ) + $psake.context.Peek().config.taskNameFormat = $format +} + +# .ExternalHelp psake.psm1-help.xml +function TaskSetup { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$setup + ) + $psake.context.Peek().taskSetupScriptBlock = $setup +} + +# .ExternalHelp psake.psm1-help.xml +function TaskTearDown { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$teardown + ) + $psake.context.Peek().taskTearDownScriptBlock = $teardown +} + +# .ExternalHelp psake.psm1-help.xml +function Framework { + [CmdletBinding()] + param( + [Parameter(Position=0,Mandatory=1)][string]$framework + ) + $psake.context.Peek().config.framework = $framework + ConfigureBuildEnvironment +} + +# .ExternalHelp psake.psm1-help.xml +function Invoke-psake { + [CmdletBinding()] + param( + [Parameter(Position = 0, Mandatory = 0)][string] $buildFile, + [Parameter(Position = 1, Mandatory = 0)][string[]] $taskList = @(), + [Parameter(Position = 2, Mandatory = 0)][string] $framework, + [Parameter(Position = 3, Mandatory = 0)][switch] $docs = $false, + [Parameter(Position = 4, Mandatory = 0)][hashtable] $parameters = @{}, + [Parameter(Position = 5, Mandatory = 0)][hashtable] $properties = @{}, + [Parameter(Position = 6, Mandatory = 0)][alias("init")][scriptblock] $initialization = {}, + [Parameter(Position = 7, Mandatory = 0)][switch] $nologo = $false + ) + try { + if (-not $nologo) { + "psake version {0}`nCopyright (c) 2010 James Kovacs`n" -f $psake.version + } + + if (!$buildFile) { + $buildFile = $psake.config_default.buildFileName + } + elseif (!(test-path $buildFile -pathType Leaf) -and (test-path $psake.config_default.buildFileName -pathType Leaf)) { + # If the $config.buildFileName file exists and the given "buildfile" isn 't found assume that the given + # $buildFile is actually the target Tasks to execute in the $config.buildFileName script. + $taskList = $buildFile.Split(', ') + $buildFile = $psake.config_default.buildFileName + } + + # Execute the build file to set up the tasks and defaults + Assert (test-path $buildFile -pathType Leaf) ($msgs.error_build_file_not_found -f $buildFile) + + $psake.build_script_file = get-item $buildFile + $psake.build_script_dir = $psake.build_script_file.DirectoryName + $psake.build_success = $false + + $psake.context.push(@{ + "taskSetupScriptBlock" = {}; + "taskTearDownScriptBlock" = {}; + "executedTasks" = new-object System.Collections.Stack; + "callStack" = new-object System.Collections.Stack; + "originalEnvPath" = $env:path; + "originalDirectory" = get-location; + "originalErrorActionPreference" = $global:ErrorActionPreference; + "tasks" = @{}; + "aliases" = @{}; + "properties" = @(); + "includes" = new-object System.Collections.Queue; + "config" = CreateConfigurationForNewContext $buildFile $framework + }) + + LoadConfiguration $psake.build_script_dir + + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + + set-location $psake.build_script_dir + + LoadModules + + $frameworkOldValue = $framework + . $psake.build_script_file.FullName + + $currentContext = $psake.context.Peek() + + if ($framework -ne $frameworkOldValue) { + writecoloredoutput $msgs.warning_deprecated_framework_variable -foregroundcolor Yellow + $currentContext.config.framework = $framework + } + + ConfigureBuildEnvironment + + while ($currentContext.includes.Count -gt 0) { + $includeFilename = $currentContext.includes.Dequeue() + . $includeFilename + } + + if ($docs) { + WriteDocumentation + CleanupEnvironment + return + } + + foreach ($key in $parameters.keys) { + if (test-path "variable:\$key") { + set-item -path "variable:\$key" -value $parameters.$key -WhatIf:$false -Confirm:$false | out-null + } else { + new-item -path "variable:\$key" -value $parameters.$key -WhatIf:$false -Confirm:$false | out-null + } + } + + # The initial dot (.) indicates that variables initialized/modified in the propertyBlock are available in the parent scope. + foreach ($propertyBlock in $currentContext.properties) { + . $propertyBlock + } + + foreach ($key in $properties.keys) { + if (test-path "variable:\$key") { + set-item -path "variable:\$key" -value $properties.$key -WhatIf:$false -Confirm:$false | out-null + } + } + + # Simple dot sourcing will not work. We have to force the script block into our + # module's scope in order to initialize variables properly. + . $MyInvocation.MyCommand.Module $initialization + + # Execute the list of tasks or the default task + if ($taskList) { + foreach ($task in $taskList) { + invoke-task $task + } + } elseif ($currentContext.tasks.default) { + invoke-task default + } else { + throw $msgs.error_no_default_task + } + + WriteColoredOutput ("`n" + $msgs.build_success + "`n") -foregroundcolor Green + + WriteTaskTimeSummary $stopwatch.Elapsed + + $psake.build_success = $true + } catch { + $currentConfig = GetCurrentConfigurationOrDefault + if ($currentConfig.verboseError) { + $error_message = "{0}: An Error Occurred. See Error Details Below: `n" -f (Get-Date) + $error_message += ("-" * 70) + "`n" + $error_message += "Error: {0}`n" -f (ResolveError $_ -Short) + $error_message += ("-" * 70) + "`n" + $error_message += ResolveError $_ + $error_message += ("-" * 70) + "`n" + $error_message += "Script Variables" + "`n" + $error_message += ("-" * 70) + "`n" + $error_message += get-variable -scope script | format-table | out-string + } else { + # ($_ | Out-String) gets error messages with source information included. + $error_message = "Error: {0}: `n{1}" -f (Get-Date), (ResolveError $_ -Short) + } + + $psake.build_success = $false + + # if we are running in a nested scope (i.e. running a psake script from a psake script) then we need to re-throw the exception + # so that the parent script will fail otherwise the parent script will report a successful build + $inNestedScope = ($psake.context.count -gt 1) + if ( $inNestedScope ) { + throw $_ + } else { + if (!$psake.run_by_psake_build_tester) { + WriteColoredOutput $error_message -foregroundcolor Red + } + } + } finally { + CleanupEnvironment + } +} + +#-- Private Module Functions --# +function WriteColoredOutput { + param( + [string] $message, + [System.ConsoleColor] $foregroundcolor + ) + + $currentConfig = GetCurrentConfigurationOrDefault + if ($currentConfig.coloredOutput -eq $true) { + if (($Host.UI -ne $null) -and ($Host.UI.RawUI -ne $null) -and ($Host.UI.RawUI.ForegroundColor -ne $null)) { + $previousColor = $Host.UI.RawUI.ForegroundColor + $Host.UI.RawUI.ForegroundColor = $foregroundcolor + } + } + + $message + + if ($previousColor -ne $null) { + $Host.UI.RawUI.ForegroundColor = $previousColor + } +} + +function LoadModules { + $currentConfig = $psake.context.peek().config + if ($currentConfig.modules) { + + $scope = $currentConfig.moduleScope + + $global = [string]::Equals($scope, "global", [StringComparison]::CurrentCultureIgnoreCase) + + $currentConfig.modules | foreach { + resolve-path $_ | foreach { + "Loading module: $_" + $module = import-module $_ -passthru -DisableNameChecking -global:$global + if (!$module) { + throw ($msgs.error_loading_module -f $_.Name) + } + } + } + "" + } +} + +function LoadConfiguration { + param( + [string] $configdir = $PSScriptRoot + ) + + $psakeConfigFilePath = (join-path $configdir "psake-config.ps1") + + if (test-path $psakeConfigFilePath -pathType Leaf) { + try { + $config = GetCurrentConfigurationOrDefault + . $psakeConfigFilePath + } catch { + throw "Error Loading Configuration from psake-config.ps1: " + $_ + } + } +} + +function GetCurrentConfigurationOrDefault() { + if ($psake.context.count -gt 0) { + return $psake.context.peek().config + } else { + return $psake.config_default + } +} + +function CreateConfigurationForNewContext { + param( + [string] $buildFile, + [string] $framework + ) + + $previousConfig = GetCurrentConfigurationOrDefault + + $config = new-object psobject -property @{ + buildFileName = $previousConfig.buildFileName; + framework = $previousConfig.framework; + taskNameFormat = $previousConfig.taskNameFormat; + verboseError = $previousConfig.verboseError; + coloredOutput = $previousConfig.coloredOutput; + modules = $previousConfig.modules; + moduleScope = $previousConfig.moduleScope; + } + + if ($framework) { + $config.framework = $framework; + } + + if ($buildFile) { + $config.buildFileName = $buildFile; + } + + return $config +} + +function ConfigureBuildEnvironment { + $framework = $psake.context.peek().config.framework + if ($framework -cmatch '^((?:\d+\.\d+)(?:\.\d+){0,1})(x86|x64){0,1}$') { + $versionPart = $matches[1] + $bitnessPart = $matches[2] + } else { + throw ($msgs.error_invalid_framework -f $framework) + } + $versions = $null + $buildToolsVersions = $null + switch ($versionPart) { + '1.0' { + $versions = @('v1.0.3705') + } + '1.1' { + $versions = @('v1.1.4322') + } + '2.0' { + $versions = @('v2.0.50727') + } + '3.0' { + $versions = @('v2.0.50727') + } + '3.5' { + $versions = @('v3.5', 'v2.0.50727') + } + '4.0' { + $versions = @('v4.0.30319') + } + '4.5.1' { + $versions = @('v4.0.30319') + $buildToolsVersions = @('12.0') + } + default { + throw ($msgs.error_unknown_framework -f $versionPart, $framework) + } + } + + $bitness = 'Framework' + if ($versionPart -ne '1.0' -and $versionPart -ne '1.1') { + switch ($bitnessPart) { + 'x86' { + $bitness = 'Framework' + $buildToolsKey = 'MSBuildToolsPath32' + } + 'x64' { + $bitness = 'Framework64' + $buildToolsKey = 'MSBuildToolsPath' + } + { [string]::IsNullOrEmpty($_) } { + $ptrSize = [System.IntPtr]::Size + switch ($ptrSize) { + 4 { + $bitness = 'Framework' + $buildToolsKey = 'MSBuildToolsPath32' + } + 8 { + $bitness = 'Framework64' + $buildToolsKey = 'MSBuildToolsPath' + } + default { + throw ($msgs.error_unknown_pointersize -f $ptrSize) + } + } + } + default { + throw ($msgs.error_unknown_bitnesspart -f $bitnessPart, $framework) + } + } + } + $frameworkDirs = @() + if ($buildToolsVersions -ne $null) { + $frameworkDirs = @($buildToolsVersions | foreach { (Get-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\MSBuild\ToolsVersions\$_" -Name $buildToolsKey).$buildToolsKey }) + } + $frameworkDirs = $frameworkDirs + @($versions | foreach { "$env:windir\Microsoft.NET\$bitness\$_\" }) + + for ($i = 0; $i -lt $frameworkDirs.Count; $i++) { + $dir = $frameworkDirs[$i] + if ($dir -Match "\$\(Registry:HKEY_LOCAL_MACHINE(.*?)@(.*)\)") { + $key = "HKLM:" + $matches[1] + $name = $matches[2] + $dir = (Get-ItemProperty -Path $key -Name $name).$name + $frameworkDirs[$i] = $dir + } + } + + $frameworkDirs | foreach { Assert (test-path $_ -pathType Container) ($msgs.error_no_framework_install_dir_found -f $_)} + + $env:path = ($frameworkDirs -join ";") + ";$env:path" + # if any error occurs in a PS function then "stop" processing immediately + # this does not effect any external programs that return a non-zero exit code + $global:ErrorActionPreference = "Stop" +} + +function CleanupEnvironment { + if ($psake.context.Count -gt 0) { + $currentContext = $psake.context.Peek() + $env:path = $currentContext.originalEnvPath + Set-Location $currentContext.originalDirectory + $global:ErrorActionPreference = $currentContext.originalErrorActionPreference + [void] $psake.context.Pop() + } +} + +function SelectObjectWithDefault +{ + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline=$true)] + [PSObject] + $InputObject, + [string] + $Name, + $Value + ) + + process { + if ($_ -eq $null) { $Value } + elseif ($_ | Get-Member -Name $Name) { + $_.$Name + } + elseif (($_ -is [Hashtable]) -and ($_.Keys -contains $Name)) { + $_.$Name + } + else { $Value } + } +} + +# borrowed from Jeffrey Snover http://blogs.msdn.com/powershell/archive/2006/12/07/resolve-error.aspx +# modified to better handle SQL errors +function ResolveError +{ + [CmdletBinding()] + param( + [Parameter(ValueFromPipeline=$true)] + $ErrorRecord=$Error[0], + [Switch] + $Short + ) + + process { + if ($_ -eq $null) { $_ = $ErrorRecord } + $ex = $_.Exception + + if (-not $Short) { + $error_message = "`nErrorRecord:{0}ErrorRecord.InvocationInfo:{1}Exception:`n{2}" + $formatted_errorRecord = $_ | format-list * -force | out-string + $formatted_invocationInfo = $_.InvocationInfo | format-list * -force | out-string + $formatted_exception = '' + + $i = 0 + while ($ex -ne $null) { + $i++ + $formatted_exception += ("$i" * 70) + "`n" + + ($ex | format-list * -force | out-string) + "`n" + $ex = $ex | SelectObjectWithDefault -Name 'InnerException' -Value $null + } + + return $error_message -f $formatted_errorRecord, $formatted_invocationInfo, $formatted_exception + } + + $lastException = @() + while ($ex -ne $null) { + $lastMessage = $ex | SelectObjectWithDefault -Name 'Message' -Value '' + $lastException += ($lastMessage -replace "`n", '') + if ($ex -is [Data.SqlClient.SqlException]) { + $lastException += "(Line [$($ex.LineNumber)] " + + "Procedure [$($ex.Procedure)] Class [$($ex.Class)] " + + " Number [$($ex.Number)] State [$($ex.State)] )" + } + $ex = $ex | SelectObjectWithDefault -Name 'InnerException' -Value $null + } + $shortException = $lastException -join ' --> ' + + $header = $null + $current = $_ + $header = (($_.InvocationInfo | + SelectObjectWithDefault -Name 'PositionMessage' -Value '') -replace "`n", ' '), + ($_ | SelectObjectWithDefault -Name 'Message' -Value ''), + ($_ | SelectObjectWithDefault -Name 'Exception' -Value '') | + ? { -not [String]::IsNullOrEmpty($_) } | + Select -First 1 + + $delimiter = '' + if ((-not [String]::IsNullOrEmpty($header)) -and + (-not [String]::IsNullOrEmpty($shortException))) + { $delimiter = ' [<<==>>] ' } + + return "$($header)$($delimiter)Exception: $($shortException)" + } +} + +function WriteDocumentation { + $currentContext = $psake.context.Peek() + + if ($currentContext.tasks.default) { + $defaultTaskDependencies = $currentContext.tasks.default.DependsOn + } else { + $defaultTaskDependencies = @() + } + + $currentContext.tasks.Keys | foreach-object { + if ($_ -eq "default") { + return + } + + $task = $currentContext.tasks.$_ + new-object PSObject -property @{ + Name = $task.Name; + Alias = $task.Alias; + Description = $task.Description; + "Depends On" = $task.DependsOn -join ", " + Default = if ($defaultTaskDependencies -contains $task.Name) { $true } + } + } | sort 'Name' | format-table -autoSize -wrap -property Name,Alias,"Depends On",Default,Description +} + +function WriteTaskTimeSummary($invokePsakeDuration) { + "-" * 70 + "Build Time Report" + "-" * 70 + $list = @() + $currentContext = $psake.context.Peek() + while ($currentContext.executedTasks.Count -gt 0) { + $taskKey = $currentContext.executedTasks.Pop() + $task = $currentContext.tasks.$taskKey + if ($taskKey -eq "default") { + continue + } + $list += new-object PSObject -property @{ + Name = $task.Name; + Duration = $task.Duration + } + } + [Array]::Reverse($list) + $list += new-object PSObject -property @{ + Name = "Total:"; + Duration = $invokePsakeDuration + } + # using "out-string | where-object" to filter out the blank line that format-table prepends + $list | format-table -autoSize -property Name,Duration | out-string -stream | where-object { $_ } +} + +DATA msgs { +convertfrom-stringdata @' + error_invalid_task_name = Task name should not be null or empty string. + error_task_name_does_not_exist = Task {0} does not exist. + error_circular_reference = Circular reference found for task {0}. + error_missing_action_parameter = Action parameter must be specified when using PreAction or PostAction parameters for task {0}. + error_corrupt_callstack = Call stack was corrupt. Expected {0}, but got {1}. + error_invalid_framework = Invalid .NET Framework version, {0} specified. + error_unknown_framework = Unknown .NET Framework version, {0} specified in {1}. + error_unknown_pointersize = Unknown pointer size ({0}) returned from System.IntPtr. + error_unknown_bitnesspart = Unknown .NET Framework bitness, {0}, specified in {1}. + error_no_framework_install_dir_found = No .NET Framework installation directory found at {0}. + error_bad_command = Error executing command {0}. + error_default_task_cannot_have_action = 'default' task cannot specify an action. + error_duplicate_task_name = Task {0} has already been defined. + error_duplicate_alias_name = Alias {0} has already been defined. + error_invalid_include_path = Unable to include {0}. File not found. + error_build_file_not_found = Could not find the build file {0}. + error_no_default_task = 'default' task required. + error_loading_module = Error loading module {0}. + warning_deprecated_framework_variable = Warning: Using global variable $framework to set .NET framework version used is deprecated. Instead use Framework function or configuration file psake-config.ps1. + required_variable_not_set = Variable {0} must be set to run task {1}. + postcondition_failed = Postcondition failed for task {0}. + precondition_was_false = Precondition was false, not executing task {0}. + continue_on_error = Error in task {0}. {1} + build_success = Build Succeeded! +'@ +} + +import-localizeddata -bindingvariable msgs -erroraction silentlycontinue + +$script:psake = @{} +$psake.version = "4.3.2" # contains the current version of psake +$psake.context = new-object system.collections.stack # holds onto the current state of all variables +$psake.run_by_psake_build_tester = $false # indicates that build is being run by psake-BuildTester +$psake.config_default = new-object psobject -property @{ + buildFileName = "default.ps1"; + framework = "4.0"; + taskNameFormat = "Executing {0}"; + verboseError = $false; + coloredOutput = $true; + modules = $null; + moduleScope = ""; +} # contains default configuration, can be overriden in psake-config.ps1 in directory with psake.psm1 or in directory with current build script + +$psake.build_success = $false # indicates that the current build was successful +$psake.build_script_file = $null # contains a System.IO.FileInfo for the current build script +$psake.build_script_dir = "" # contains a string with fully-qualified path to current build script + +LoadConfiguration + +export-modulemember -function Invoke-psake, Invoke-Task, Task, Properties, Include, FormatTaskName, TaskSetup, TaskTearDown, Framework, Assert, Exec -variable psake From be1ac32541d2cf2516888cd2b0436523c2f95f3b Mon Sep 17 00:00:00 2001 From: Scooletz Date: Sun, 18 Jan 2015 22:23:58 +0100 Subject: [PATCH 16/21] User model: color scheme id introduced --- NGitLab/NGitLab/Models/User.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NGitLab/NGitLab/Models/User.cs b/NGitLab/NGitLab/Models/User.cs index b7f65a37..aa952a34 100644 --- a/NGitLab/NGitLab/Models/User.cs +++ b/NGitLab/NGitLab/Models/User.cs @@ -47,8 +47,8 @@ public class User [DataMember(Name = "bio")] public string Bio; - [DataMember(Name="dark_scheme")] - public bool DarkScheme; + [DataMember(Name = "color_scheme_id")] + public int ColorSchemeId; [DataMember(Name="theme_id")] public int ThemeId; From b1968349c6ec5b6e7c7c0e1fded7313feb376a1a Mon Sep 17 00:00:00 2001 From: Michel Courtine Date: Thu, 5 Feb 2015 03:52:15 +0100 Subject: [PATCH 17/21] Fixed Exception when deserializing Json Iso8601 containing Timezone info --- NGitLab/NGitLab/Impl/SimpleJson.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/NGitLab/NGitLab/Impl/SimpleJson.cs b/NGitLab/NGitLab/Impl/SimpleJson.cs index f44e0482..966e7f20 100644 --- a/NGitLab/NGitLab/Impl/SimpleJson.cs +++ b/NGitLab/NGitLab/Impl/SimpleJson.cs @@ -1244,6 +1244,7 @@ class PocoJsonSerializerStrategy : IJsonSerializerStrategy private static readonly string[] Iso8601Format = new string[] { + @"yyyy-MM-dd\THH:mm:ss.FFFFFFFK", @"yyyy-MM-dd\THH:mm:ss.FFFFFFF\Z", @"yyyy-MM-dd\THH:mm:ss\Z", @"yyyy-MM-dd\THH:mm:ssK" From db740a53768c0ec29b78f323d780fc37e73c5a8c Mon Sep 17 00:00:00 2001 From: Michel Courtine Date: Thu, 5 Feb 2015 17:55:21 +0100 Subject: [PATCH 18/21] Added support for Groups (aka Namespace) --- NGitLab/NGitLab/GitLabClient.cs | 2 ++ NGitLab/NGitLab/INamespaceClient.cs | 19 +++++++++++ NGitLab/NGitLab/Impl/NamespaceClient.cs | 41 +++++++++++++++++++++++ NGitLab/NGitLab/Models/Namespace.cs | 2 +- NGitLab/NGitLab/Models/NamespaceCreate.cs | 20 +++++++++++ NGitLab/NGitLab/NGitLab.csproj | 4 +++ 6 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 NGitLab/NGitLab/INamespaceClient.cs create mode 100644 NGitLab/NGitLab/Impl/NamespaceClient.cs create mode 100644 NGitLab/NGitLab/Models/NamespaceCreate.cs diff --git a/NGitLab/NGitLab/GitLabClient.cs b/NGitLab/NGitLab/GitLabClient.cs index d2405901..38e69c0f 100644 --- a/NGitLab/NGitLab/GitLabClient.cs +++ b/NGitLab/NGitLab/GitLabClient.cs @@ -10,6 +10,7 @@ private GitLabClient(string hostUrl, string apiToken) Users = new UserClient(_api); Projects = new ProjectClient(_api); Issues = new IssueClient(_api); + Groups = new NamespaceClient(_api); } public static GitLabClient Connect(string hostUrl, string apiToken) @@ -22,6 +23,7 @@ public static GitLabClient Connect(string hostUrl, string apiToken) public readonly IUserClient Users; public readonly IProjectClient Projects; public readonly IIssueClient Issues; + public readonly INamespaceClient Groups; public IRepositoryClient GetRepository(int projectId) { diff --git a/NGitLab/NGitLab/INamespaceClient.cs b/NGitLab/NGitLab/INamespaceClient.cs new file mode 100644 index 00000000..be4a57c0 --- /dev/null +++ b/NGitLab/NGitLab/INamespaceClient.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using NGitLab.Models; + +namespace NGitLab +{ + public interface INamespaceClient + { + /// + /// Get a list of projects accessible by the authenticated user. + /// + IEnumerable Accessible { get; } + + Namespace this[int id] { get; } + + Namespace Create(NamespaceCreate group); + + void Delete(int id); + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab/Impl/NamespaceClient.cs b/NGitLab/NGitLab/Impl/NamespaceClient.cs new file mode 100644 index 00000000..c9648296 --- /dev/null +++ b/NGitLab/NGitLab/Impl/NamespaceClient.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; +using NGitLab.Models; + +namespace NGitLab.Impl +{ + public class NamespaceClient : INamespaceClient + { + private readonly API _api; + + public NamespaceClient(API api) + { + _api = api; + } + + public IEnumerable Accessible + { + get + { + return _api.Get().GetAll(Namespace.Url); + } + } + + public Namespace this[int id] + { + get + { + return _api.Get().To(Namespace.Url + "/" + id); + } + } + + public Namespace Create(NamespaceCreate group) + { + return _api.Post().With(group).To(Namespace.Url); + } + + public void Delete(int id) + { + _api.Delete().To(Namespace.Url + "/" + id); + } + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab/Models/Namespace.cs b/NGitLab/NGitLab/Models/Namespace.cs index 121d57f3..3d61957c 100644 --- a/NGitLab/NGitLab/Models/Namespace.cs +++ b/NGitLab/NGitLab/Models/Namespace.cs @@ -6,7 +6,7 @@ namespace NGitLab.Models [DataContract] public class Namespace { - public const string URL = "/groups"; + public const string Url = "/groups"; [DataMember(Name = "id")] public int Id; diff --git a/NGitLab/NGitLab/Models/NamespaceCreate.cs b/NGitLab/NGitLab/Models/NamespaceCreate.cs new file mode 100644 index 00000000..9b2b3f9d --- /dev/null +++ b/NGitLab/NGitLab/Models/NamespaceCreate.cs @@ -0,0 +1,20 @@ +using System.ComponentModel.DataAnnotations; +using System.Runtime.Serialization; + +namespace NGitLab.Models +{ + [DataContract] + public class NamespaceCreate + { + [Required] + [DataMember(Name = "name")] + public string Name; + + [Required] + [DataMember(Name = "path")] + public string Path; + + [DataMember(Name = "description")] + public string Description; + } +} \ No newline at end of file diff --git a/NGitLab/NGitLab/NGitLab.csproj b/NGitLab/NGitLab/NGitLab.csproj index b6ffca66..38cf8e22 100644 --- a/NGitLab/NGitLab/NGitLab.csproj +++ b/NGitLab/NGitLab/NGitLab.csproj @@ -13,6 +13,7 @@ 512 8.0.30703 2.0 + 2.0 true @@ -100,6 +101,9 @@ + + + From a1a840c27c6cf0c3db7af332233fae3076afa9b1 Mon Sep 17 00:00:00 2001 From: Michel Courtine Date: Sat, 7 Feb 2015 16:54:43 +0100 Subject: [PATCH 19/21] Added support for error message reporting from gitlab In HttpRequestor.cs, req.GetResponse() threw exceptions without reading the response from the server The json error response is now deserialized and the message is thrown to the caller when available --- NGitLab/NGitLab/Impl/HttpRequestor.cs | 32 ++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/NGitLab/NGitLab/Impl/HttpRequestor.cs b/NGitLab/NGitLab/Impl/HttpRequestor.cs index dbce9d57..5fee6ff5 100644 --- a/NGitLab/NGitLab/Impl/HttpRequestor.cs +++ b/NGitLab/NGitLab/Impl/HttpRequestor.cs @@ -7,6 +7,11 @@ namespace NGitLab.Impl { + internal class JsonError + { + public string message; + } + public class HttpRequestor { private readonly API _root; @@ -45,13 +50,34 @@ public void Stream(string tailAPIUrl, Action parser) req.Headers.Add("Content-Length", "0"); } - using (var response = req.GetResponse()) + try + { + using (var response = req.GetResponse()) + { + using (var stream = response.GetResponseStream()) + { + parser(stream); + } + } + } + catch (WebException wex) { - using (var stream = response.GetResponseStream()) + if (wex.Response != null) { - parser(stream); + using (var errorResponse = (HttpWebResponse)wex.Response) + { + using (var reader = new StreamReader(errorResponse.GetResponseStream())) + { + string jsonString = reader.ReadToEnd(); + JsonError jsonError = SimpleJson.DeserializeObject(jsonString); + throw new Exception(string.Format("The remote server returned an error ({0}): {1}", errorResponse.StatusCode, jsonError.message)); + } + } } + else + throw wex; } + } public IEnumerable GetAll(string tailUrl) From 882f6303baafaca10eb7e825f9fe04e706f794bf Mon Sep 17 00:00:00 2001 From: Michel Courtine Date: Sat, 7 Feb 2015 16:55:24 +0100 Subject: [PATCH 20/21] Fixed Project.Delete trying to deserialize a json bool into a Project --- NGitLab/NGitLab/IProjectClient.cs | 2 +- NGitLab/NGitLab/Impl/ProjectClient.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/NGitLab/NGitLab/IProjectClient.cs b/NGitLab/NGitLab/IProjectClient.cs index 99ae3150..2ec84660 100644 --- a/NGitLab/NGitLab/IProjectClient.cs +++ b/NGitLab/NGitLab/IProjectClient.cs @@ -24,6 +24,6 @@ public interface IProjectClient Project Create(ProjectCreate project); - void Delete(int id); + bool Delete(int id); } } \ No newline at end of file diff --git a/NGitLab/NGitLab/Impl/ProjectClient.cs b/NGitLab/NGitLab/Impl/ProjectClient.cs index f789c826..60fd080d 100644 --- a/NGitLab/NGitLab/Impl/ProjectClient.cs +++ b/NGitLab/NGitLab/Impl/ProjectClient.cs @@ -49,9 +49,9 @@ public Project Create(ProjectCreate project) return _api.Post().With(project).To(Project.Url); } - public void Delete(int id) + public bool Delete(int id) { - _api.Delete().To(Project.Url + "/" + id); + return _api.Delete().To(Project.Url + "/" + id); } } } \ No newline at end of file From 9527f25f0780c5f58ed2f8b4f58429b0abc0f2ad Mon Sep 17 00:00:00 2001 From: Scooletz Date: Sun, 15 Feb 2015 18:17:19 +0100 Subject: [PATCH 21/21] warnings & contracts: clean up --- .../RepositoryClient/FilesClientTests.cs | 5 +--- NGitLab/NGitLab/Impl/HttpRequestor.cs | 25 +++++++++++-------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/NGitLab/NGitLab.Tests/RepositoryClient/FilesClientTests.cs b/NGitLab/NGitLab.Tests/RepositoryClient/FilesClientTests.cs index 9c1518ab..2d020188 100644 --- a/NGitLab/NGitLab.Tests/RepositoryClient/FilesClientTests.cs +++ b/NGitLab/NGitLab.Tests/RepositoryClient/FilesClientTests.cs @@ -1,11 +1,8 @@ -using NGitLab.Models; - -namespace NGitLab.Tests.RepositoryClient +namespace NGitLab.Tests.RepositoryClient { public class FilesClientTests { private readonly IFilesClient _client; - private FileData[] _files; public FilesClientTests() { diff --git a/NGitLab/NGitLab/Impl/HttpRequestor.cs b/NGitLab/NGitLab/Impl/HttpRequestor.cs index 5fee6ff5..7be8dd0e 100644 --- a/NGitLab/NGitLab/Impl/HttpRequestor.cs +++ b/NGitLab/NGitLab/Impl/HttpRequestor.cs @@ -4,12 +4,17 @@ using System.IO; using System.Linq; using System.Net; +using System.Runtime.Serialization; namespace NGitLab.Impl { + [DataContract] internal class JsonError { - public string message; +#pragma warning disable 649 + [DataMember(Name = "message")] + public string Message; +#pragma warning restore 649 } public class HttpRequestor @@ -69,8 +74,8 @@ public void Stream(string tailAPIUrl, Action parser) using (var reader = new StreamReader(errorResponse.GetResponseStream())) { string jsonString = reader.ReadToEnd(); - JsonError jsonError = SimpleJson.DeserializeObject(jsonString); - throw new Exception(string.Format("The remote server returned an error ({0}): {1}", errorResponse.StatusCode, jsonError.message)); + var jsonError = SimpleJson.DeserializeObject(jsonString); + throw new Exception(string.Format("The remote server returned an error ({0}): {1}", errorResponse.StatusCode, jsonError.Message)); } } } @@ -98,7 +103,7 @@ public Enumerable(string apiToken, Uri startUrl) public IEnumerator GetEnumerator() { - return new Enumerator(_apiToken, _startUrl); + return new Enumerator(_apiToken, _startUrl); } IEnumerator IEnumerable.GetEnumerator() @@ -106,14 +111,12 @@ IEnumerator IEnumerable.GetEnumerator() return GetEnumerator(); } - private class Enumerator : IEnumerator + private class Enumerator : IEnumerator { private readonly string _apiToken; private Uri _nextUrlToLoad; private readonly List _buffer = new List(); - private bool _finished; - public Enumerator(string apiToken, Uri startUrl) { _apiToken = apiToken; @@ -140,12 +143,12 @@ public bool MoveNext() { // ; rel="next", ; rel="first", ; rel="last" var link = response.Headers["Link"]; - + string[] nextLink = null; if (string.IsNullOrEmpty(link) == false) - nextLink = link.Split(',') - .Select(l => l.Split(';')) - .FirstOrDefault(pair => pair[1].Contains("next")); + nextLink = link.Split(',') + .Select(l => l.Split(';')) + .FirstOrDefault(pair => pair[1].Contains("next")); if (nextLink != null) {