diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5f299a8 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,17 @@ +# These files are text and should be normalized (convert crlf => lf) +*.cs text diff=csharp +*.xaml text +*.csproj text +*.sln text +*.tt text +*.ps1 text +*.cmd text +*.msbuild text +*.md text + +# Images should be treated as binary +# (binary is a macro for -text -diff) +*.png binary +*.jepg binary + +*.sdf binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..daa9e0f --- /dev/null +++ b/.gitignore @@ -0,0 +1,35 @@ +# Build Folders (you can keep bin if you'd like, to store dlls and pdbs) +[Bb]in/ +[Oo]bj/ + +# ApprovalTests (received files) +*.received.txt + +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Dd]ebug/ +[Rr]elease/ +x64/ + +# ReSharper is a .NET coding add-in +_ReSharper* + +# NuGet Packages Directory +packages + +# Others +*.Cache +[Ss]tyle[Cc]op.* + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML diff --git a/src/.nuget/NuGet.Config b/src/.nuget/NuGet.Config new file mode 100644 index 0000000..67f8ea0 --- /dev/null +++ b/src/.nuget/NuGet.Config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/.nuget/NuGet.exe b/src/.nuget/NuGet.exe new file mode 100644 index 0000000..9cba6ed Binary files /dev/null and b/src/.nuget/NuGet.exe differ diff --git a/src/.nuget/NuGet.targets b/src/.nuget/NuGet.targets new file mode 100644 index 0000000..2c3545b --- /dev/null +++ b/src/.nuget/NuGet.targets @@ -0,0 +1,151 @@ + + + + $(MSBuildProjectDirectory)\..\ + + + false + + + false + + + true + + + false + + + + + + + + + + + $([System.IO.Path]::Combine($(SolutionDir), ".nuget")) + + + + + $(SolutionDir).nuget + + + + packages.$(MSBuildProjectName.Replace(' ', '_')).config + + + + + + $(PackagesProjectConfig) + + + + + packages.config + + + + + + + $(NuGetToolsPath)\NuGet.exe + @(PackageSource) + + "$(NuGetExePath)" + mono --runtime=v4.0.30319 $(NuGetExePath) + + $(TargetDir.Trim('\\')) + + -RequireConsent + -NonInteractive + + "$(SolutionDir) " + "$(SolutionDir)" + + + $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir) + $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols + + + + RestorePackages; + $(BuildDependsOn); + + + + + $(BuildDependsOn); + BuildPackage; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/SemanticReleaseNotes.Tests/Examples.Example_A.approved.txt b/src/SemanticReleaseNotes.Tests/Examples.Example_A.approved.txt new file mode 100644 index 0000000..40d0fb2 --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/Examples.Example_A.approved.txt @@ -0,0 +1,29 @@ +ReleaseNotes + [Section + (null,null, + Some + (Summary + "Incremental release designed to provide an update to some of the core plugins. +"), + Some + (Items + [Item + (null, + Summary + "Release Checker: Now gives you a breakdown of exactly what you are missing. +New", + null); + Item + (null, + Summary + "Structured Layout: An alternative layout engine that allows developers to control layout. +New", + null); + Item + (null, + Summary + "Timeline: Comes with an additional grid view to show the same data. +Changed", + null); + Item + (null, + Summary + "Ajax: Fix that crashed poll in Chrome and IE due to log/trace statement. +Fix", + null)]))] \ No newline at end of file diff --git a/src/SemanticReleaseNotes.Tests/Examples.Example_B.approved.txt b/src/SemanticReleaseNotes.Tests/Examples.Example_B.approved.txt new file mode 100644 index 0000000..51ba810 --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/Examples.Example_B.approved.txt @@ -0,0 +1,36 @@ +ReleaseNotes + [Section + (null,null, + Some + (Summary + "Incremental release designed to provide an update to some of the core plugins. +"), + null); + Section + (Some (Name " System"),Some (Priority 1),null, + Some + (Items + [Item + (null, + Summary + "*Release Checker*: Now gives you a breakdown of exactly what you are missing. +New", + null); + Item + (null, + Summary + "*Structured Layout*: An alternative layout engine that allows developers to control layout. +New", + null)])); + Section + (Some (Name " Plugin"),Some (Priority 1),null, + Some + (Items + [Item + (null, + Summary + "*Timeline*: Comes with an additional grid view to show the same data. +Changed", + null); + Item + (null, + Summary + "*Ajax*: Fix that crashed poll in Chrome and IE due to log/trace statement. +Fix", + null)]))] \ No newline at end of file diff --git a/src/SemanticReleaseNotes.Tests/Examples.Example_C.approved.txt b/src/SemanticReleaseNotes.Tests/Examples.Example_C.approved.txt new file mode 100644 index 0000000..3e519d5 --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/Examples.Example_C.approved.txt @@ -0,0 +1,43 @@ +ReleaseNotes + [Section + (null,null, + Some + (Summary + "Incremental release designed to provide an update to some of the core plugins."), + Some + (Items + [Item + (null, + Summary + "*Example*: You can have global issues that aren't grouped to a section", + null)])); + Section + (Some (Name " System"),Some (Priority 1), + Some (Summary "This description is specific to system section."), + Some + (Items + [Item + (null, + Summary + "*Release Checker*: Now gives you a +new breakdown of exactly what you are missing.", + null); + Item + (null, + Summary + "*Structured Layout*: A +new alternative layout engine that allows developers to control layout.", + null)])); + Section + (Some (Name " Plugin"),Some (Priority 1), + Some (Summary "This description is specific to plugin section."), + Some + (Items + [Item + (null, + Summary + "*Timeline*: Comes with an additional grid view to show the same data. +Changed", + null); + Item + (null, + Summary + "*Ajax*: +Fix that crashed poll in Chrome and IE due to log/trace statement. [[i1234][http://getglimpse.com]]", + null)]))] \ No newline at end of file diff --git a/src/SemanticReleaseNotes.Tests/Examples.Example_D.approved.txt b/src/SemanticReleaseNotes.Tests/Examples.Example_D.approved.txt new file mode 100644 index 0000000..df5e293 --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/Examples.Example_D.approved.txt @@ -0,0 +1,46 @@ +ReleaseNotes + [Section + (null,null, + Some + (Summary + "Incremental release designed to provide an update to some of the core plugins."), + Some + (Items + [Item + (Some (Priority 1), + Summary + "*Example*: You can have global issues that aren't grouped to a section", + null)])); + Section + (Some + (Name " System [[icon][http://getglimpse.com/release/icon/core.png]]"), + Some (Priority 1), + Some (Summary "This description is specific to system section."), + Some + (Items + [Item + (Some (Priority 3), + Summary + "*Release Checker*: Now gives you a breakdown of exactly what you are missing. +New", + null); + Item + (Some (Priority 2), + Summary + "*Structured Layout*: An alternative layout engine that allows developers to control layout. +New", + null)])); + Section + (Some (Name " Plugin [[icon][http://getglimpse.com/release/icon/mvc.png]]"), + Some (Priority 1), + Some (Summary "This description is specific to plugin section."), + Some + (Items + [Item + (Some (Priority 1), + Summary + "*Timeline*: Comes with an additional grid view to show the same data. +Changed", + null); + Item + (Some (Priority 1), + Summary + "*Ajax*: Fix that crashed poll in Chrome and IE due to log/trace statement. +Fix [[i1234][http://getglimpse.com]]", + null)]))] \ No newline at end of file diff --git a/src/SemanticReleaseNotes.Tests/Examples.cs b/src/SemanticReleaseNotes.Tests/Examples.cs new file mode 100644 index 0000000..4558447 --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/Examples.cs @@ -0,0 +1,86 @@ +using System.Linq; +using ApprovalTests; +using NUnit.Framework; + +namespace SemanticReleaseNotes.Tests +{ + [TestFixture] + public class Examples + { + [Test] + public void Example_A() + { + var input = @" +Incremental release designed to provide an update to some of the core plugins. + + - Release Checker: Now gives you a breakdown of exactly what you are missing. +New + - Structured Layout: An alternative layout engine that allows developers to control layout. +New + - Timeline: Comes with an additional grid view to show the same data. +Changed + - Ajax: Fix that crashed poll in Chrome and IE due to log/trace statement. +Fix +"; + var result = Parser.ParseAST(input); + Approvals.Verify(Parser.PrettyPrint(result)); + } + + [Test] + public void Example_B() + { + var input = @" +Incremental release designed to provide an update to some of the core plugins. + +# System + - *Release Checker*: Now gives you a breakdown of exactly what you are missing. +New + - *Structured Layout*: An alternative layout engine that allows developers to control layout. +New + +# Plugin + - *Timeline*: Comes with an additional grid view to show the same data. +Changed + - *Ajax*: Fix that crashed poll in Chrome and IE due to log/trace statement. +Fix +"; + var result = Parser.ParseAST(input); + Approvals.Verify(Parser.PrettyPrint(result)); + } + + [Test] + public void Example_C() + { + var input = @" +Incremental release designed to provide an update to some of the core plugins. + - *Example*: You can have global issues that aren't grouped to a section + +# System +This description is specific to system section. + - *Release Checker*: Now gives you a +new breakdown of exactly what you are missing. + - *Structured Layout*: A +new alternative layout engine that allows developers to control layout. + +# Plugin +This description is specific to plugin section. + - *Timeline*: Comes with an additional grid view to show the same data. +Changed + - *Ajax*: +Fix that crashed poll in Chrome and IE due to log/trace statement. [[i1234][http://getglimpse.com]] +"; + var result = Parser.ParseAST(input); + Approvals.Verify(Parser.PrettyPrint(result)); + } + + [Test] + public void Example_D() + { + var input = @" +Incremental release designed to provide an update to some of the core plugins. + 1. *Example*: You can have global issues that aren't grouped to a section + +# System [[icon][http://getglimpse.com/release/icon/core.png]] +This description is specific to system section. + 3. *Release Checker*: Now gives you a breakdown of exactly what you are missing. +New + 2. *Structured Layout*: An alternative layout engine that allows developers to control layout. +New + +# Plugin [[icon][http://getglimpse.com/release/icon/mvc.png]] +This description is specific to plugin section. + 1. *Timeline*: Comes with an additional grid view to show the same data. +Changed + 1. *Ajax*: Fix that crashed poll in Chrome and IE due to log/trace statement. +Fix [[i1234][http://getglimpse.com]] +"; + var result = Parser.ParseAST(input); + Approvals.Verify(Parser.PrettyPrint(result)); + } + + } +} \ No newline at end of file diff --git a/src/SemanticReleaseNotes.Tests/Properties/AssemblyInfo.cs b/src/SemanticReleaseNotes.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..ae1cb00 --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,39 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using ApprovalTests.Reporters; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("SemanticReleaseNotes.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("SemanticReleaseNotes.Tests")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("eee93377-e39d-4834-83bd-3351d7dd7b99")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] + +[assembly: UseReporter(typeof(DiffReporter))] diff --git a/src/SemanticReleaseNotes.Tests/SemanticReleaseNotes.Tests.csproj b/src/SemanticReleaseNotes.Tests/SemanticReleaseNotes.Tests.csproj new file mode 100644 index 0000000..b7c76d0 --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/SemanticReleaseNotes.Tests.csproj @@ -0,0 +1,128 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {E54CFD45-C5C8-456A-99AE-04E810410E23} + Library + Properties + SemanticReleaseNotes.Tests + SemanticReleaseNotes.Tests + v4.0 + 512 + ..\ + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\ApprovalTests.3.0.5\lib\net40\ApprovalTests.dll + + + ..\packages\ApprovalUtilities.3.0.5\lib\net35\ApprovalUtilities.dll + + + ..\packages\FParsec-Big-Data-Edition.1.0.1\lib\net40-client\FParsec.dll + + + ..\packages\FParsec-Big-Data-Edition.1.0.1\lib\net40-client\FParsecCS.dll + + + True + ..\packages\FSharp.Core.4.0.0\lib\FSharp.Core.dll + + + ..\packages\Newtonsoft.Json.6.0.1\lib\net40\Newtonsoft.Json.dll + + + ..\packages\NUnit.2.6.3\lib\nunit.framework.dll + + + + + + + + + + + + + + + + + + + + + {E8D4FB83-FB86-416B-B2D4-8ED4E5E360E9} + SemanticReleaseNotes + + + + + Examples.cs + + + Examples.cs + + + Examples.cs + + + Examples.cs + + + Syntax.cs + + + Syntax.cs + + + Syntax.cs + + + Syntax.cs + + + Syntax.cs + + + Syntax.cs + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/SemanticReleaseNotes.Tests/Syntax.ItemsAST.approved.txt b/src/SemanticReleaseNotes.Tests/Syntax.ItemsAST.approved.txt new file mode 100644 index 0000000..e47c0d7 --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/Syntax.ItemsAST.approved.txt @@ -0,0 +1,9 @@ +ReleaseNotes + [Section + (null,null,null, + Some + (Items + [Item (null,Summary "This is the _first_ *list* item.",null); + Item (null,Summary "This is the **second** __list__ item.",null); + Item (null,Summary "This is the `third` list item.",null); + Item (null,Summary "This is the [forth](?) list item.",null)]))] \ No newline at end of file diff --git a/src/SemanticReleaseNotes.Tests/Syntax.ItemsJSON.approved.txt b/src/SemanticReleaseNotes.Tests/Syntax.ItemsJSON.approved.txt new file mode 100644 index 0000000..2d6cf20 --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/Syntax.ItemsJSON.approved.txt @@ -0,0 +1,16 @@ +{ + "items": [ + { + "summary": "This is the _first_ *list* item." + }, + { + "summary": "This is the **second** __list__ item." + }, + { + "summary": "This is the `third` list item." + }, + { + "summary": "This is the [forth](?) list item." + } + ] +} \ No newline at end of file diff --git a/src/SemanticReleaseNotes.Tests/Syntax.PriorityAST.approved.txt b/src/SemanticReleaseNotes.Tests/Syntax.PriorityAST.approved.txt new file mode 100644 index 0000000..a49d27d --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/Syntax.PriorityAST.approved.txt @@ -0,0 +1,26 @@ +ReleaseNotes + [Section + (null,null,null, + Some + (Items + [Item + (Some (Priority 1),Summary "This is a High priority list item.", + null); + Item + (Some (Priority 1),Summary "This is a High priority list item. ", + null); + Item + (Some (Priority 2),Summary "This is a Normal priority list item. ", + null); + Item + (Some (Priority 1),Summary "This is a High priority list item. ", + null); + Item + (Some (Priority 2),Summary "This is a Normal priority list item.", + null); + Item + (Some (Priority 3),Summary "This is a Minor priority list item.", + null); + Item + (Some (Priority 3),Summary "This is a Minor priority list item. ", + null)]))] \ No newline at end of file diff --git a/src/SemanticReleaseNotes.Tests/Syntax.SectionsAST.approved.txt b/src/SemanticReleaseNotes.Tests/Syntax.SectionsAST.approved.txt new file mode 100644 index 0000000..74df0d2 --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/Syntax.SectionsAST.approved.txt @@ -0,0 +1,20 @@ +ReleaseNotes + [Section + (Some (Name " Section"),Some (Priority 1), + Some (Summary "This is the summary for Section."), + Some + (Items + [Item (null,Summary "This is a Section scoped first list item.",null); + Item + (null,Summary "This is a Section scoped second list item.",null)])); + Section + (Some (Name " Other Section"),Some (Priority 1), + Some (Summary "This is the summary for Other Section."), + Some + (Items + [Item + (null,Summary "This is a Other Section scoped first list item.", + null); + Item + (null,Summary "This is a Other Section scoped second list item.", + null)]))] \ No newline at end of file diff --git a/src/SemanticReleaseNotes.Tests/Syntax.SummaryAST.approved.txt b/src/SemanticReleaseNotes.Tests/Syntax.SummaryAST.approved.txt new file mode 100644 index 0000000..fa4f0be --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/Syntax.SummaryAST.approved.txt @@ -0,0 +1,13 @@ +ReleaseNotes + [Section + (null,null, + Some + (Summary + "This is a _project_ summary with two paragraphs. +Lorem ipsum dolor sit amet consectetuer **adipiscing** elit. +Aliquam hendreritmi posuere lectus. + +Vestibulum `enim wisi` viverra nec fringilla in laoreet +vitae risus. Donec sit amet nisl. Aliquam [semper](?) ipsum +sit amet velit."), + null)] \ No newline at end of file diff --git a/src/SemanticReleaseNotes.Tests/Syntax.SummaryJSON.approved.txt b/src/SemanticReleaseNotes.Tests/Syntax.SummaryJSON.approved.txt new file mode 100644 index 0000000..33a699a --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/Syntax.SummaryJSON.approved.txt @@ -0,0 +1,4 @@ +{ + "summary": "This is a _project_ summary with two paragraphs. \nLorem ipsum dolor sit amet consectetuer **adipiscing** elit. \nAliquam hendreritmi posuere lectus.\n\nVestibulum `enim wisi` viverra nec fringilla in laoreet\nvitae risus. Donec sit amet nisl. Aliquam [semper](?) ipsum\nsit amet velit.", + "items": [] +} \ No newline at end of file diff --git a/src/SemanticReleaseNotes.Tests/Syntax.cs b/src/SemanticReleaseNotes.Tests/Syntax.cs new file mode 100644 index 0000000..ffc383a --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/Syntax.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using ApprovalTests; +using NUnit.Framework; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using SemanticReleaseNotes.Tests.TestHelpers; + +namespace SemanticReleaseNotes.Tests +{ + /// + /// Syntax examples sourced from: http://semanticreleasenotes.org/#Syntax [Sat.Feb.8.2014] + /// + [TestFixture] + public class Syntax + { + private static class SyntaxExamples + { + /// + /// A summary is one or more paragraphs of text. + /// + public static string Summary = +@"This is a _project_ summary with two paragraphs. +Lorem ipsum dolor sit amet consectetuer **adipiscing** elit. +Aliquam hendreritmi posuere lectus. + +Vestibulum `enim wisi` viverra nec fringilla in laoreet +vitae risus. Donec sit amet nisl. Aliquam [semper](?) ipsum +sit amet velit."; + + /// + /// Items are indicated via a standard Markdown + /// ordered or unordered list. + /// + public static string Items = @" +- This is the _first_ *list* item. +- This is the **second** __list__ item. +- This is the `third` list item. +- This is the [forth](?) list item."; + + } + + /// + /// The required to mimic + /// the JSON output on http://semanticreleasenotes.org + /// + private static readonly JsonSerializerSettings semanticReleaseNotesJsonSerializerSettings = + new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver(), + Formatting = Formatting.Indented, + NullValueHandling = NullValueHandling.Ignore, + }; + + [Test] + public void SummaryAST() + { + var input = SyntaxExamples.Summary; + + var result = Parser.ParseAST(input); + + //Assert.AreEqual(input.NormalizeLineEndings(),result); + Approvals.Verify(Parser.PrettyPrint(result)); + } + + [Test] + public void SummaryJSON() + { + var input = SyntaxExamples.Summary; + + var result = Parser.Parse(input); + + var json = JsonConvert.SerializeObject(result, semanticReleaseNotesJsonSerializerSettings); + + Approvals.Verify(json); + } + + [Test] + public void ItemsAST() + { + var input = SyntaxExamples.Items; + + var result = Parser.ParseAST(input); + + Approvals.Verify(Parser.PrettyPrint(result)); + } + + [Test] + public void ItemsJSON() + { + var input = SyntaxExamples.Items; + + var result = Parser.Parse(input); + + var json = JsonConvert.SerializeObject(result, semanticReleaseNotesJsonSerializerSettings); + + Approvals.Verify(json); + } + + /// + /// Priority is indicated via a standard Markdown + /// ordered list. + /// + [Test] + public void PriorityAST() + { + var input = @" +1. This is a High priority list item. +1. This is a High priority list item. +2. This is a Normal priority list item. +1. This is a High priority list item. +2. This is a Normal priority list item. +3. This is a Minor priority list item. +3. This is a Minor priority list item. "; + + var result = Parser.ParseAST(input); + + Approvals.Verify(Parser.PrettyPrint(result)); + } + + /// + /// A Section can be arbitrary in nature and specific + /// to the release notes of the application. Sections + /// are indicated via a standard Markdown header. + /// + [Test] + public void SectionsAST() + { + var input = @" +# Section +This is the summary for Section. + - This is a Section scoped first list item. + - This is a Section scoped second list item. + +# Other Section +This is the summary for Other Section. + - This is a Other Section scoped first list item. + - This is a Other Section scoped second list item. +"; + + var result = Parser.ParseAST(input); + + Approvals.Verify(Parser.PrettyPrint(result)); + } + + /// + /// + /// In some cases we want to have the one document that + /// describes many releases. In this case, the syntax + /// simply allows you to define a heading which is the + /// Version Number. If you use this feature in conjunction + /// with sections, Section headers are also altered. + /// + /// + /// For the purposes of interpretation of the version number, + /// it is assumed that you are using Semantic Visioning - + /// http://semver.org/. Normal Markdown headers are used + /// to describe the version number for each release and + /// the scope of reach release (which item, etc makes up + /// the release). + /// + /// + [Test] + public void Release() + { + Assert.Inconclusive("Examples not yet specified."); + } + } +} \ No newline at end of file diff --git a/src/SemanticReleaseNotes.Tests/TestHelpers/StringExtensions.cs b/src/SemanticReleaseNotes.Tests/TestHelpers/StringExtensions.cs new file mode 100644 index 0000000..958cb1a --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/TestHelpers/StringExtensions.cs @@ -0,0 +1,12 @@ +using System.Linq; + +namespace SemanticReleaseNotes.Tests.TestHelpers +{ + public static class StringExtensions + { + public static string NormalizeLineEndings(this string src) + { + return (src ?? string.Empty).Replace("\r\n", "\n"); + } + } +} diff --git a/src/SemanticReleaseNotes.Tests/packages.config b/src/SemanticReleaseNotes.Tests/packages.config new file mode 100644 index 0000000..813bbac --- /dev/null +++ b/src/SemanticReleaseNotes.Tests/packages.config @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/SemanticReleaseNotes.sln b/src/SemanticReleaseNotes.sln new file mode 100644 index 0000000..7122501 --- /dev/null +++ b/src/SemanticReleaseNotes.sln @@ -0,0 +1,33 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2010 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{D9C61DF4-BEB2-4305-87CA-73FF05D7B64F}" + ProjectSection(SolutionItems) = preProject + .nuget\NuGet.Config = .nuget\NuGet.Config + .nuget\NuGet.exe = .nuget\NuGet.exe + .nuget\NuGet.targets = .nuget\NuGet.targets + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SemanticReleaseNotes.Tests", "SemanticReleaseNotes.Tests\SemanticReleaseNotes.Tests.csproj", "{E54CFD45-C5C8-456A-99AE-04E810410E23}" +EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "SemanticReleaseNotes", "SemanticReleaseNotes\SemanticReleaseNotes.fsproj", "{E8D4FB83-FB86-416B-B2D4-8ED4E5E360E9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E54CFD45-C5C8-456A-99AE-04E810410E23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E54CFD45-C5C8-456A-99AE-04E810410E23}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E54CFD45-C5C8-456A-99AE-04E810410E23}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E54CFD45-C5C8-456A-99AE-04E810410E23}.Release|Any CPU.Build.0 = Release|Any CPU + {E8D4FB83-FB86-416B-B2D4-8ED4E5E360E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E8D4FB83-FB86-416B-B2D4-8ED4E5E360E9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E8D4FB83-FB86-416B-B2D4-8ED4E5E360E9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E8D4FB83-FB86-416B-B2D4-8ED4E5E360E9}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/SemanticReleaseNotes/AST.fs b/src/SemanticReleaseNotes/AST.fs new file mode 100644 index 0000000..63b87a5 --- /dev/null +++ b/src/SemanticReleaseNotes/AST.fs @@ -0,0 +1,44 @@ +module AST + +// Notes sourced from: http://semanticreleasenotes.org/#Syntax [Sat.Feb.8.2014] + +// At its most basic level, SRN should be able to take a paragraph +// and interpret that as the summary of a given release. +type Summary = Summary of string + +// The release note should be able to let you define the importance +// feature or item that has been defined. +type Priority = Priority of int + +// The release note should be able to indicate what category an item is. +// For instance, "+New", "+Change", "+Bug-Fix", "+Developer". +type Category = Category of string + +// A release note should be able to show which "items" are included +// in a given release. Items typically include features, bug fixes, +// or improvements. +type Item = Item of Priority option * Summary * Category option +type Items = Items of Item list + +// Lets the author group items which represent logical sections within +// the application. These can be arbitrary in nature and specific to +// the release notes which application is for. +type Name = Name of string +type Section = + | Section of Name option * Priority option * Summary option * Items option + +// TBD: As SRN allows for many releases to be defined within the one +// block of text, the system needs to provide a means by which an +// individual release can be identified. +// type Release = ??? + +type ReleaseNotes = + | ReleaseNotes of Section list + +(* +let ToObjectModel(ReleaseNotes n) = + let (Summary summary, Items items) = n + let a = new SemanticReleaseNotes.ReleaseNotes() + a.Summary <- summary + a +*) diff --git a/src/SemanticReleaseNotes/ASTToObjectModelVisitor.fs b/src/SemanticReleaseNotes/ASTToObjectModelVisitor.fs new file mode 100644 index 0000000..4839e02 --- /dev/null +++ b/src/SemanticReleaseNotes/ASTToObjectModelVisitor.fs @@ -0,0 +1,33 @@ +namespace SemanticReleaseNotes.Visitors + +module ASTToObjectModelVisitor = + + open AST + open SemanticReleaseNotes + + let private ItemToObjectModel (item:Item) = + let (Item (priority,(Summary summary),category)) = item + let result = new ObjectModel.Item() + result.Summary <- summary + result.Category <- null + result + + let Visit (ast:ReleaseNotes) : ObjectModel.ReleaseNotes = + let (ReleaseNotes notes) = ast + let result = new ObjectModel.ReleaseNotes() + match notes with + | (section::tail) -> + let (Section (head,priority,summary,items)) = section + match summary with + | Some (Summary s) -> result.Summary <- s + | None -> result.Summary <- null + match items with + | Some (Items n) -> + n + |> List.map ItemToObjectModel + |> List.map (fun x -> result.Items.Add(x)) + |> ignore + | None -> result.Items.Clear() + result + | [] -> + result diff --git a/src/SemanticReleaseNotes/Parser.fs b/src/SemanticReleaseNotes/Parser.fs new file mode 100644 index 0000000..be18115 --- /dev/null +++ b/src/SemanticReleaseNotes/Parser.fs @@ -0,0 +1,129 @@ +module Parser + +open System +open FParsec + +open AST +open SemanticReleaseNotes.Visitors + +// convenience type for locking down generic type inference issues +// from: http://www.quanttec.com/fparsec/tutorial.html#fs-value-restriction +type private State = unit + +let private ws = spaces + +let private ch c = pchar c +let private ch_ws c = ch c .>> ws +let private ws_ch_ws c = ws >>. ch c .>> ws + +let private str s = pstring s +let private str_ws s = str s .>> ws +let private ws_str_ws s = ws >>. str s .>> ws + +// make this compiler directive condition true to trace the parsers +#if xDEBUG +let () (p: Parser<_,_>) label : Parser<_,_> = + fun stream -> + printfn "%A: Entering %s" stream.Position label + let reply = p stream + printfn "%A: Leaving %s (%A)" stream.Position label reply.Status + reply +#else +let () (p: Parser<_,_>) label : Parser<_,_> = + fun stream -> p stream +#endif + +(* +let private anyStringUntil_ch c = manySatisfy ((<>) c) +let private between_ch cStart cEnd p = between (ch cStart) (ch cEnd) p +let private string_between_ch cStart cEnd = between_ch cStart cEnd (anyStringUntil_ch cEnd) // "stringSurroundedBy" +//let private quotedString = string_between_ch '"' '"' +let private hexn n = manyMinMaxSatisfy n n isHex +let private guidFromTuple t = + let (((a,b),c),d),e = t + let s = sprintf "%s-%s-%s-%s-%s" a b c d e + Guid.Parse(s) +let private guid : Parser = (hexn 8 .>> ch '-' .>>. hexn 4 .>> ch '-' .>>. hexn 4 .>> ch '-' .>>. hexn 4 .>> ch '-' .>>. hexn 12) |>> guidFromTuple // "guid" +let private guid_between_str sStart sEnd = between (str sStart) (str sEnd) guid + +let private emptyIfNone items = + match items with + | Some x -> x + | None -> [] +*) + +//let private restOfLineAsText skipNewline = restOfLine skipNewline |>> Text "text" + +// ------- +let private newline_as_string = newline |>> fun x -> x.ToString() +let private eof_as_string = eof >>. preturn "" +let private eol = newline_as_string <|> eof_as_string + +let private priority = ws >>. pint32 .>> skipString ". " |>> Priority "item priority" + +let private hn = many1Satisfy (fun c -> match c with '#' -> true | _ -> false) |>> (fun h -> h.Length) |>> Priority "heading priority" +let private name = restOfLine true "name" +let private heading = ws >>. hn .>>. name "heading" + +let private empty_line = newline >>. preturn "" "empty line" +let private summaryLine = lookAhead (ws >>. noneOf "-+*#123456789") >>. many1CharsTill (noneOf "\r\n") eol "summary line" +let private join (s:string) (list:string list) = String.Join(s, list) +let private summary = many1 (summaryLine <|> empty_line) |>> join "\n" |>> Summary "summary" + +let private unorderedItem = tuple3 (preturn None) (ws >>. anyOf "-+*" >>. ws >>. restOfLine true |>> Summary) (preturn None) "unordered item" +let private orderedItem = tuple3 (priority |>> Some) (restOfLine true |>> Summary) (preturn None) "ordered item" +let private item = (attempt orderedItem <|> unorderedItem) |>> Item +let private items = many1 (attempt item) |>> Items "items" + +let private resolveSectionTuple (optHeading:(Priority*string) option) (summary:Summary option) (items:Items option) = + let (optPriority,optName) = match optHeading with + | Some(p,n) -> (Some(p),Some(Name n)) + | None -> (None,None) + (optName, optPriority, summary, items) + +// Ensure that a section has at least one of name, summary, or items +let private ensureSectionResultIsValid (p: Parser) = + fun stream -> + let reply = p stream + if reply.Status = ReplyStatus.Ok then + let (name,priority,summary,items) = reply.Result + if name.IsSome || summary.IsSome || items.IsSome then + Reply(reply.Result) + else + Reply(Error, expected "Either a heading, a summary paragraph, or a list of items.") + else + Reply(reply.Status,reply.Error) + +let private sectionContent = pipe3 (opt heading) (opt summary) (opt items) resolveSectionTuple +let private section = ensureSectionResultIsValid sectionContent |>> Section "section" + +let private releaseNotes = ws >>. many1 section .>> ws .>> eof |>> ReleaseNotes "release notes" + +let private parser = releaseNotes .>> eof + +exception ParseError of string * ParserError + +type ParseException (message:string, context:ParserError) = + inherit ApplicationException(message, null) + let Context = context + +let ParseAST str = + match run parser str with + | Success(result, _, _) -> result + | Failure(errorMsg, errorContext, _) -> raise (new ParseException(errorMsg, errorContext)) + +let Parse str = ParseAST str |> ASTToObjectModelVisitor.Visit + +let PrettyPrint a = sprintf "%A" a + +#if DEBUG +let Test p str = + match run p str with + | Success(result, _, _) -> printfn "Success: %A" result + | Failure(errorMsg, _, _) -> printfn "Failure: %s" errorMsg + +let Run p str = + match run p str with + | Success(result, _, _) -> result + | Failure(errorMsg, errorContext, _) -> raise (new ParseException(errorMsg, errorContext)) +#endif \ No newline at end of file diff --git a/src/SemanticReleaseNotes/ReleaseNotes.fs b/src/SemanticReleaseNotes/ReleaseNotes.fs new file mode 100644 index 0000000..5f0a79f --- /dev/null +++ b/src/SemanticReleaseNotes/ReleaseNotes.fs @@ -0,0 +1,31 @@ +namespace SemanticReleaseNotes.ObjectModel + +open System +open System.Collections.Generic + +type Item() = + let mutable priority = new Nullable() + let mutable summary = "" + let mutable category = "" + + member this.Priority + with get() = priority + and set(value) = priority <- value + + member this.Summary + with get() = summary + and set(value) = summary <- value + + member this.Category + with get() = category + and set(value) = category <- value + +type ReleaseNotes() = + let mutable summary = "" + let mutable items = new List() + member this.Summary + with get() = summary + and set value = summary <- value + member this.Items + with get() = items + and set value = items <- value diff --git a/src/SemanticReleaseNotes/SemanticReleaseNotes.fsproj b/src/SemanticReleaseNotes/SemanticReleaseNotes.fsproj new file mode 100644 index 0000000..c33caaa --- /dev/null +++ b/src/SemanticReleaseNotes/SemanticReleaseNotes.fsproj @@ -0,0 +1,74 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {e8d4fb83-fb86-416b-b2d4-8ed4e5e360e9} + Library + SemanticReleaseNotes + SemanticReleaseNotes + v4.0 + SemanticReleaseNotes + ..\ + true + + + true + full + false + false + bin\Debug\ + DEBUG;TRACE + 3 + bin\Debug\SemanticReleaseNotes.XML + + + pdbonly + true + true + bin\Release\ + TRACE + 3 + bin\Release\SemanticReleaseNotes.XML + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + + + + + + + + ..\packages\FParsec-Big-Data-Edition.1.0.1\lib\net40-client\FParsec.dll + True + + + ..\packages\FParsec-Big-Data-Edition.1.0.1\lib\net40-client\FParsecCS.dll + True + + + + + + + + + \ No newline at end of file diff --git a/src/SemanticReleaseNotes/packages.config b/src/SemanticReleaseNotes/packages.config new file mode 100644 index 0000000..2a39bd6 --- /dev/null +++ b/src/SemanticReleaseNotes/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file