From 6267c7effaa4d4f0512a24be1915add106c60798 Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Fri, 25 Oct 2024 08:26:18 +0100 Subject: [PATCH 01/15] WIP --- src/Library.fs | 4 ++++ tests/UnitTests.fs | 24 ++++++++++++++++++++++++ tests/changelogs/CHANGELOG_unreleased.md | 19 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 tests/changelogs/CHANGELOG_unreleased.md diff --git a/src/Library.fs b/src/Library.fs index a48d7c6..fcbb221 100644 --- a/src/Library.fs +++ b/src/Library.fs @@ -85,6 +85,9 @@ type ParseChangeLogs() = [] member val UnreleasedChangelog: ITaskItem = null with get, set + + [] + member val UnreleasedReleaseNotes: string = null with get, set [] member val CurrentReleaseChangelog: ITaskItem = null with get, set @@ -132,6 +135,7 @@ type ParseChangeLogs() = | null -> Ok() | unreleased -> this.UnreleasedChangelog <- unreleased.ToTaskItem() + this.UnreleasedReleaseNotes <- unreleased.SubSectionCollection.ToMarkdown() Ok() member this.ProcessReleases(changelog: Changelog) = diff --git a/tests/UnitTests.fs b/tests/UnitTests.fs index 43a096e..205336c 100644 --- a/tests/UnitTests.fs +++ b/tests/UnitTests.fs @@ -130,6 +130,30 @@ type UnitTests() = ### Changed +- Changed something in the package +- Updated the target framework""" + ) + + [] + member this.``task adds pre-release when an unreleased section is present``() = + let myTask = ParseChangeLogs(ChangelogFile = Workspace.changelogs.``CHANGELOG.md``) + + myTask.BuildEngine <- this.context.BuildEngine.Object + + let success = myTask.Execute() + %success.Should().BeTrue "Should have successfully parsed the changelog data" + + %myTask.CurrentReleaseChangelog + + %myTask.LatestReleaseNotes + .Should() + .Be( + """### Added + +- Created the package + +### Changed + - Changed something in the package - Updated the target framework""" ) diff --git a/tests/changelogs/CHANGELOG_unreleased.md b/tests/changelogs/CHANGELOG_unreleased.md new file mode 100644 index 0000000..9d447ad --- /dev/null +++ b/tests/changelogs/CHANGELOG_unreleased.md @@ -0,0 +1,19 @@ +# Changelog + +## [Unreleased] + +### Removed + +- A test removal line +- And another removal + +## [0.1.0] - 2022-01-13 + +### Added + +- Created the package + +### Changed + +- Changed something in the package +- Updated the target framework From 9cdc26d0ddeab7318c6362b54cc308588e5db5ff Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Fri, 25 Oct 2024 16:20:18 +0100 Subject: [PATCH 02/15] Add very simple unreleased version --- src/Library.fs | 11 +++++++++++ tests/UnitTests.fs | 27 ++++++++++++--------------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/Library.fs b/src/Library.fs index fcbb221..dd15be1 100644 --- a/src/Library.fs +++ b/src/Library.fs @@ -137,6 +137,13 @@ type ParseChangeLogs() = this.UnreleasedChangelog <- unreleased.ToTaskItem() this.UnreleasedReleaseNotes <- unreleased.SubSectionCollection.ToMarkdown() Ok() + + member this.UpdateUnreleasedVersion(latestVersion : SemVersion) = + match this.UnreleasedChangelog with + | null -> () + | _ -> + let newUnreleased = latestVersion.WithPrereleaseParsedFrom "alpha" |> _.WithPatch(latestVersion.Patch + 1) + this.UnreleasedChangelog.ItemSpec <- newUnreleased.ToString() member this.ProcessReleases(changelog: Changelog) = let releases = @@ -161,7 +168,11 @@ type ParseChangeLogs() = this.CurrentReleaseChangelog <- mapped[0] this.AllReleasedChangelogs <- mapped this.LatestReleaseNotes <- latestRelease.collection.ToMarkdown() + + this.UpdateUnreleasedVersion(latestRelease.version) + Ok() + /// /// Helper method to log an error with the given log data. diff --git a/tests/UnitTests.fs b/tests/UnitTests.fs index 205336c..f663065 100644 --- a/tests/UnitTests.fs +++ b/tests/UnitTests.fs @@ -134,26 +134,23 @@ type UnitTests() = - Updated the target framework""" ) + %myTask.UnreleasedReleaseNotes + .Should() + .BeLineEndingEquivalent( + """### Removed + +- A test removal line +- And another removal""") [] - member this.``task adds pre-release when an unreleased section is present``() = + member this.``task produces correct versions``() = let myTask = ParseChangeLogs(ChangelogFile = Workspace.changelogs.``CHANGELOG.md``) myTask.BuildEngine <- this.context.BuildEngine.Object let success = myTask.Execute() %success.Should().BeTrue "Should have successfully parsed the changelog data" - - %myTask.CurrentReleaseChangelog - - %myTask.LatestReleaseNotes - .Should() - .Be( - """### Added -- Created the package - -### Changed - -- Changed something in the package -- Updated the target framework""" - ) + %myTask.CurrentReleaseChangelog.ItemSpec.Should().Be("0.1.0") + %myTask.UnreleasedChangelog.ItemSpec.Should().Be("0.1.1-alpha") + + \ No newline at end of file From d186da31da7f58fa17833a05f071ecdb8726c10a Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Sat, 26 Oct 2024 10:41:47 +0100 Subject: [PATCH 03/15] Add simple unreleased version logic - Simply take the latest version, increment patch, add -alpha. - Handle changelogs with no releases. - Hanlde changelogs with no releases nor unreleased - Add targets from #5 which conditionally use the unreleased version in packaging --- src/Library.fs | 45 +++++----- src/build/Core.targets | 15 +++- src/build/Ionide.KeepAChangelog.Tasks.props | 6 ++ tests/Ionide.KeepAChangelog.Tasks.Test.fsproj | 1 + tests/UnitTests.fs | 84 +++++++++++++++---- tests/changelogs/CHANGELOG.md | 7 -- tests/changelogs/CHANGELOG_detailed.md | 7 ++ tests/changelogs/CHANGELOG_empty.md | 3 + tests/changelogs/CHANGELOG_unreleased.md | 10 --- tests/fixtures/CHANGELOG.md | 7 -- tests/fixtures/CHANGELOG_KeepAChangelog.md | 2 - tests/fixtures/CHANGELOG_unreleased.md | 19 +++++ tests/fixtures/WorksForUnreleased.fsproj | 20 +++++ 13 files changed, 160 insertions(+), 66 deletions(-) create mode 100644 src/build/Ionide.KeepAChangelog.Tasks.props create mode 100644 tests/changelogs/CHANGELOG_empty.md create mode 100644 tests/fixtures/CHANGELOG_unreleased.md create mode 100644 tests/fixtures/WorksForUnreleased.fsproj diff --git a/src/Library.fs b/src/Library.fs index dd15be1..7099682 100644 --- a/src/Library.fs +++ b/src/Library.fs @@ -85,9 +85,9 @@ type ParseChangeLogs() = [] member val UnreleasedChangelog: ITaskItem = null with get, set - + [] - member val UnreleasedReleaseNotes: string = null with get, set + member val UnreleasedReleaseNotes: string = null with get, set [] member val CurrentReleaseChangelog: ITaskItem = null with get, set @@ -131,48 +131,53 @@ type ParseChangeLogs() = Error() member this.ReadUnreleasedSection(changelog: Changelog) = - match changelog.SectionUnreleased with - | null -> Ok() - | unreleased -> + match changelog.SectionUnreleased, changelog.SectionUnreleased.MarkdownTitle with + | null, _ + | _, "" -> Ok() + | unreleased, _ -> this.UnreleasedChangelog <- unreleased.ToTaskItem() this.UnreleasedReleaseNotes <- unreleased.SubSectionCollection.ToMarkdown() Ok() - - member this.UpdateUnreleasedVersion(latestVersion : SemVersion) = + + member this.UpdateUnreleasedVersion(latestVersion: SemVersion) = match this.UnreleasedChangelog with | null -> () | _ -> - let newUnreleased = latestVersion.WithPrereleaseParsedFrom "alpha" |> _.WithPatch(latestVersion.Patch + 1) + let newUnreleased = + latestVersion.WithPrereleaseParsedFrom "alpha" + |> _.WithPatch(latestVersion.Patch + 1) + this.UnreleasedChangelog.ItemSpec <- newUnreleased.ToString() member this.ProcessReleases(changelog: Changelog) = let releases = changelog.SectionCollection.Unwrapped() |> Seq.sortByDescending _.version - |> Seq.toArray - - let latestRelease = releases |> (fun x -> x[0]) + |> Seq.toList let mapped = releases - |> Array.map (fun x -> + |> List.map (fun x -> let taskItem = TaskItem(x.version.ToString()) taskItem.SetMetadata("Date", x.date.ToString("yyyy-MM-dd")) for (key, value) in x.collection.ToTaskItemMetadata() do taskItem.SetMetadata(key, value) - taskItem :> ITaskItem + (x, taskItem :> ITaskItem) ) - this.CurrentReleaseChangelog <- mapped[0] - this.AllReleasedChangelogs <- mapped - this.LatestReleaseNotes <- latestRelease.collection.ToMarkdown() - - this.UpdateUnreleasedVersion(latestRelease.version) - + match mapped with + | (latestRelease, latestTaskItem) :: _ -> + this.CurrentReleaseChangelog <- latestTaskItem + this.AllReleasedChangelogs <- mapped |> List.map snd |> Array.ofList + this.LatestReleaseNotes <- latestRelease.collection.ToMarkdown() + this.UpdateUnreleasedVersion(latestRelease.version) + | _ -> + this.AllReleasedChangelogs <- [||] + this.UpdateUnreleasedVersion(SemVersion(0, 0, 0)) + Ok() - /// /// Helper method to log an error with the given log data. diff --git a/src/build/Core.targets b/src/build/Core.targets index 671f75c..578a5e2 100644 --- a/src/build/Core.targets +++ b/src/build/Core.targets @@ -18,6 +18,7 @@ + @@ -26,15 +27,25 @@ + %(CurrentReleaseChangelog.Identity) %(CurrentReleaseChangelog.Identity) @(LatestReleaseNotes) + <_ReleaseDate>%(CurrentReleaseChangelog.Date) + + + + %(UnreleasedChangelog.Identity) + %(UnreleasedChangelog.Identity) + @(UnreleasedReleaseNotes) + <_ReleaseDate>%(UnreleasedChangelog.Date) + - + <_Parameter1>BuildDate - <_Parameter2>%(CurrentReleaseChangelog.Date) + <_Parameter2>%(_ReleaseDate) diff --git a/src/build/Ionide.KeepAChangelog.Tasks.props b/src/build/Ionide.KeepAChangelog.Tasks.props new file mode 100644 index 0000000..355bdc0 --- /dev/null +++ b/src/build/Ionide.KeepAChangelog.Tasks.props @@ -0,0 +1,6 @@ + + + + true + + diff --git a/tests/Ionide.KeepAChangelog.Tasks.Test.fsproj b/tests/Ionide.KeepAChangelog.Tasks.Test.fsproj index bc17ce0..6668db6 100644 --- a/tests/Ionide.KeepAChangelog.Tasks.Test.fsproj +++ b/tests/Ionide.KeepAChangelog.Tasks.Test.fsproj @@ -33,6 +33,7 @@ + diff --git a/tests/UnitTests.fs b/tests/UnitTests.fs index f663065..f7a1326 100644 --- a/tests/UnitTests.fs +++ b/tests/UnitTests.fs @@ -92,7 +92,7 @@ type UnitTests() = myTask.BuildEngine <- this.context.BuildEngine.Object let success = myTask.Execute() - %success.Should().BeTrue "Should have successfully parsed the changelog data" + %success.Should().BeTrue("Should have successfully parsed the changelog data") %myTask.AllReleasedChangelogs.Length.Should().Be(9, "Should have 9 versions") %myTask.CurrentReleaseChangelog.ItemSpec @@ -112,45 +112,93 @@ type UnitTests() = |> Seq.cast |> _.Should().Contain("Date", "Should have date metadata")) + %myTask.UnreleasedChangelog.ItemSpec + .Should() + .Be("0.1.9-alpha", "Should have the alpha prefix from a patch release") + + %(myTask.UnreleasedChangelog.MetadataNames + |> Seq.cast + |> _.Should().Contain("Removed", "Should have removed metadata")) + [] member this.``task produces expected markdown``() = - let myTask = ParseChangeLogs(ChangelogFile = Workspace.changelogs.``CHANGELOG.md``) + let myTask = + ParseChangeLogs(ChangelogFile = Workspace.changelogs.``CHANGELOG_detailed.md``) myTask.BuildEngine <- this.context.BuildEngine.Object let success = myTask.Execute() - %success.Should().BeTrue "Should have successfully parsed the changelog data" + %success.Should().BeTrue("Should have successfully parsed the changelog data") %myTask.LatestReleaseNotes .Should() .BeLineEndingEquivalent( - """### Added - -- Created the package - -### Changed + """### Changed -- Changed something in the package -- Updated the target framework""" +- Minor packaging fix for non-Core MSBuild versions""" ) - + %myTask.UnreleasedReleaseNotes .Should() .BeLineEndingEquivalent( """### Removed - A test removal line -- And another removal""") +- And another removal""" + ) + [] - member this.``task produces correct versions``() = + member this.``task correctly processes a changelog with no unreleased``() = let myTask = ParseChangeLogs(ChangelogFile = Workspace.changelogs.``CHANGELOG.md``) myTask.BuildEngine <- this.context.BuildEngine.Object let success = myTask.Execute() - %success.Should().BeTrue "Should have successfully parsed the changelog data" + %success.Should().BeTrue("Should have successfully parsed the changelog data") + + %myTask.CurrentReleaseChangelog.ItemSpec + .Should() + .Be("0.1.0", "It is the latest release") + + %myTask.LatestReleaseNotes.Should().NotBeNull().And.NotBeEmpty() + + %myTask.AllReleasedChangelogs + .Should() + .HaveLength(1, "There is only a single release section") - %myTask.CurrentReleaseChangelog.ItemSpec.Should().Be("0.1.0") - %myTask.UnreleasedChangelog.ItemSpec.Should().Be("0.1.1-alpha") - - \ No newline at end of file + %myTask.UnreleasedChangelog.Should().BeNull("There is no unreleased section") + %myTask.UnreleasedReleaseNotes.Should().BeNull("There is no unreleased section") + + [] + member this.``task correctly processes a changelog with only unreleased``() = + let myTask = + ParseChangeLogs(ChangelogFile = Workspace.changelogs.``CHANGELOG_unreleased.md``) + + myTask.BuildEngine <- this.context.BuildEngine.Object + + let success = myTask.Execute() + %success.Should().BeTrue("Should have successfully parsed the changelog data") + %myTask.CurrentReleaseChangelog.Should().BeNull("There are no released sections") + %myTask.LatestReleaseNotes.Should().BeNull("There are no released sections") + %myTask.AllReleasedChangelogs.Should().BeEmpty("There are no released sections") + + %myTask.UnreleasedChangelog.ItemSpec + .Should() + .Be("0.0.1-alpha", "There is no previous version, so it starts from 0.0.0") + + %myTask.UnreleasedReleaseNotes.Should().NotBeNull().And.NotBeEmpty() + + [] + member this.``task correctly processes a changelog with only introduction``() = + let myTask = + ParseChangeLogs(ChangelogFile = Workspace.changelogs.``CHANGELOG_empty.md``) + + myTask.BuildEngine <- this.context.BuildEngine.Object + + let success = myTask.Execute() + %success.Should().BeTrue("Should have successfully parsed the changelog data") + %myTask.CurrentReleaseChangelog.Should().BeNull("There are no released sections") + %myTask.LatestReleaseNotes.Should().BeNull("There are no released sections") + %myTask.AllReleasedChangelogs.Should().BeEmpty("There are no released sections") + %myTask.UnreleasedChangelog.Should().BeNull("There is no unreleased section") + %myTask.UnreleasedReleaseNotes.Should().BeNull("There is no unreleased section") diff --git a/tests/changelogs/CHANGELOG.md b/tests/changelogs/CHANGELOG.md index 9d447ad..5f396c8 100644 --- a/tests/changelogs/CHANGELOG.md +++ b/tests/changelogs/CHANGELOG.md @@ -1,12 +1,5 @@ # Changelog -## [Unreleased] - -### Removed - -- A test removal line -- And another removal - ## [0.1.0] - 2022-01-13 ### Added diff --git a/tests/changelogs/CHANGELOG_detailed.md b/tests/changelogs/CHANGELOG_detailed.md index 8bc7ed5..dc25472 100644 --- a/tests/changelogs/CHANGELOG_detailed.md +++ b/tests/changelogs/CHANGELOG_detailed.md @@ -1,5 +1,12 @@ # Changelog +## [Unreleased] + +### Removed + +- A test removal line +- And another removal + ## [0.1.8] - 2022-03-31 ### Changed diff --git a/tests/changelogs/CHANGELOG_empty.md b/tests/changelogs/CHANGELOG_empty.md new file mode 100644 index 0000000..3f46209 --- /dev/null +++ b/tests/changelogs/CHANGELOG_empty.md @@ -0,0 +1,3 @@ +# Changelog + +Some notes in the header but no sections diff --git a/tests/changelogs/CHANGELOG_unreleased.md b/tests/changelogs/CHANGELOG_unreleased.md index 9d447ad..ccf8ee6 100644 --- a/tests/changelogs/CHANGELOG_unreleased.md +++ b/tests/changelogs/CHANGELOG_unreleased.md @@ -7,13 +7,3 @@ - A test removal line - And another removal -## [0.1.0] - 2022-01-13 - -### Added - -- Created the package - -### Changed - -- Changed something in the package -- Updated the target framework diff --git a/tests/fixtures/CHANGELOG.md b/tests/fixtures/CHANGELOG.md index 9d447ad..5f396c8 100644 --- a/tests/fixtures/CHANGELOG.md +++ b/tests/fixtures/CHANGELOG.md @@ -1,12 +1,5 @@ # Changelog -## [Unreleased] - -### Removed - -- A test removal line -- And another removal - ## [0.1.0] - 2022-01-13 ### Added diff --git a/tests/fixtures/CHANGELOG_KeepAChangelog.md b/tests/fixtures/CHANGELOG_KeepAChangelog.md index 5c2288c..54aa7d4 100644 --- a/tests/fixtures/CHANGELOG_KeepAChangelog.md +++ b/tests/fixtures/CHANGELOG_KeepAChangelog.md @@ -1,7 +1,5 @@ # Changelog -## [Unreleased] - ## [0.1.0] - 2022-01-13 ### Added diff --git a/tests/fixtures/CHANGELOG_unreleased.md b/tests/fixtures/CHANGELOG_unreleased.md new file mode 100644 index 0000000..9d447ad --- /dev/null +++ b/tests/fixtures/CHANGELOG_unreleased.md @@ -0,0 +1,19 @@ +# Changelog + +## [Unreleased] + +### Removed + +- A test removal line +- And another removal + +## [0.1.0] - 2022-01-13 + +### Added + +- Created the package + +### Changed + +- Changed something in the package +- Updated the target framework diff --git a/tests/fixtures/WorksForUnreleased.fsproj b/tests/fixtures/WorksForUnreleased.fsproj new file mode 100644 index 0000000..6fec7c1 --- /dev/null +++ b/tests/fixtures/WorksForUnreleased.fsproj @@ -0,0 +1,20 @@ + + + + + net6.0 + + + + CHANGELOG_unreleased.md + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + From acf83da64111699d8517112d3e50ebca0fd37b19 Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Sat, 26 Oct 2024 11:19:24 +0100 Subject: [PATCH 04/15] Reorganise task, and null instead of empty array --- src/Library.fs | 69 ++++++++++++++++++++++++---------------------- tests/UnitTests.fs | 4 +-- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/src/Library.fs b/src/Library.fs index 7099682..beca480 100644 --- a/src/Library.fs +++ b/src/Library.fs @@ -4,6 +4,7 @@ open System open System.Globalization open System.Runtime.CompilerServices open System.Text +open FsToolkit.ErrorHandling.Operator.Result open Microsoft.Build.Utilities open Microsoft.Build.Framework open System.IO @@ -107,7 +108,8 @@ type ParseChangeLogs() = let! changelog = this.ParseChangelog file do! this.ReadUnreleasedSection changelog - do! this.ProcessReleases changelog + let latestRelease = this.ProcessReleases changelog + do! this.UpdateUnreleasedVersion latestRelease return true } @@ -131,23 +133,14 @@ type ParseChangeLogs() = Error() member this.ReadUnreleasedSection(changelog: Changelog) = - match changelog.SectionUnreleased, changelog.SectionUnreleased.MarkdownTitle with - | null, _ - | _, "" -> Ok() - | unreleased, _ -> - this.UnreleasedChangelog <- unreleased.ToTaskItem() - this.UnreleasedReleaseNotes <- unreleased.SubSectionCollection.ToMarkdown() - Ok() + let unreleasedSection = changelog.SectionUnreleased - member this.UpdateUnreleasedVersion(latestVersion: SemVersion) = - match this.UnreleasedChangelog with - | null -> () + match unreleasedSection.MarkdownTitle with + | "" -> Ok() | _ -> - let newUnreleased = - latestVersion.WithPrereleaseParsedFrom "alpha" - |> _.WithPatch(latestVersion.Patch + 1) - - this.UnreleasedChangelog.ItemSpec <- newUnreleased.ToString() + this.UnreleasedChangelog <- unreleasedSection.ToTaskItem() + this.UnreleasedReleaseNotes <- unreleasedSection.SubSectionCollection.ToMarkdown() + Ok() member this.ProcessReleases(changelog: Changelog) = let releases = @@ -155,29 +148,39 @@ type ParseChangeLogs() = |> Seq.sortByDescending _.version |> Seq.toList - let mapped = - releases - |> List.map (fun x -> - let taskItem = TaskItem(x.version.ToString()) - taskItem.SetMetadata("Date", x.date.ToString("yyyy-MM-dd")) + match releases with + | [] -> None + | latestRelease :: _ -> + let mapped = + releases + |> List.map (fun x -> + let taskItem = TaskItem(x.version.ToString()) + taskItem.SetMetadata("Date", x.date.ToString("yyyy-MM-dd")) - for (key, value) in x.collection.ToTaskItemMetadata() do - taskItem.SetMetadata(key, value) + for (key, value) in x.collection.ToTaskItemMetadata() do + taskItem.SetMetadata(key, value) - (x, taskItem :> ITaskItem) - ) + taskItem :> ITaskItem + ) + |> Array.ofList - match mapped with - | (latestRelease, latestTaskItem) :: _ -> - this.CurrentReleaseChangelog <- latestTaskItem - this.AllReleasedChangelogs <- mapped |> List.map snd |> Array.ofList + this.CurrentReleaseChangelog <- mapped[0] + this.AllReleasedChangelogs <- mapped this.LatestReleaseNotes <- latestRelease.collection.ToMarkdown() - this.UpdateUnreleasedVersion(latestRelease.version) + Some(latestRelease.version) + + member this.UpdateUnreleasedVersion(latestVersion: SemVersion option) = + let latestVersion = latestVersion |> Option.defaultValue (SemVersion(0, 0, 0)) + + match this.UnreleasedChangelog with + | null -> Ok() | _ -> - this.AllReleasedChangelogs <- [||] - this.UpdateUnreleasedVersion(SemVersion(0, 0, 0)) + let newUnreleased = + latestVersion.WithPrereleaseParsedFrom "alpha" + |> _.WithPatch(latestVersion.Patch + 1) - Ok() + this.UnreleasedChangelog.ItemSpec <- newUnreleased.ToString() + Ok() /// /// Helper method to log an error with the given log data. diff --git a/tests/UnitTests.fs b/tests/UnitTests.fs index f7a1326..90fa427 100644 --- a/tests/UnitTests.fs +++ b/tests/UnitTests.fs @@ -180,7 +180,7 @@ type UnitTests() = %success.Should().BeTrue("Should have successfully parsed the changelog data") %myTask.CurrentReleaseChangelog.Should().BeNull("There are no released sections") %myTask.LatestReleaseNotes.Should().BeNull("There are no released sections") - %myTask.AllReleasedChangelogs.Should().BeEmpty("There are no released sections") + %myTask.AllReleasedChangelogs.Should().BeNull("There are no released sections") %myTask.UnreleasedChangelog.ItemSpec .Should() @@ -199,6 +199,6 @@ type UnitTests() = %success.Should().BeTrue("Should have successfully parsed the changelog data") %myTask.CurrentReleaseChangelog.Should().BeNull("There are no released sections") %myTask.LatestReleaseNotes.Should().BeNull("There are no released sections") - %myTask.AllReleasedChangelogs.Should().BeEmpty("There are no released sections") + %myTask.AllReleasedChangelogs.Should().BeNull("There are no released sections") %myTask.UnreleasedChangelog.Should().BeNull("There is no unreleased section") %myTask.UnreleasedReleaseNotes.Should().BeNull("There is no unreleased section") From 16ad1dfe038bab85def9410be27ac8c7c5faed03 Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Sat, 26 Oct 2024 14:06:41 +0100 Subject: [PATCH 05/15] Add a pre-release integration test --- tests/IntegrationTests.fs | 23 +++++++++++++++++++++++ tests/fixtures/WorksForUnreleased.fsproj | 4 ++-- 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/IntegrationTests.fs b/tests/IntegrationTests.fs index 19ab843..e162c47 100644 --- a/tests/IntegrationTests.fs +++ b/tests/IntegrationTests.fs @@ -167,6 +167,29 @@ type IntegrationTests() = "PackageReleaseNotes": "" } } +""" + ) + |> ignore + } + [] + member this.``generates a pre-release version if changelog has unreleased section``() : Task = + task { + let projectName = "WorksForUnreleased.fsproj" + + this.AddPackageReference projectName + + let! struct (stdout, _) = Utils.packAndGetPackageProperties projectName + + stdout + .Should() + .BeLineEndingEquivalent( + """{ + "Properties": { + "Version": "0.1.1-alpha", + "PackageVersion": "0.1.1-alpha", + "PackageReleaseNotes": "### Removed\n\n- A test removal line\n- And another removal" + } +} """ ) |> ignore diff --git a/tests/fixtures/WorksForUnreleased.fsproj b/tests/fixtures/WorksForUnreleased.fsproj index 6fec7c1..2532691 100644 --- a/tests/fixtures/WorksForUnreleased.fsproj +++ b/tests/fixtures/WorksForUnreleased.fsproj @@ -10,8 +10,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all From 908f9568cade73cff7badb09071f57a6d3d4a336 Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Sat, 26 Oct 2024 14:27:33 +0100 Subject: [PATCH 06/15] Simplify integration testing and improve errors --- tests/IntegrationTests.fs | 70 ++++++++++--------- .../FailIfChangelogDoesNotExist.fsproj | 2 +- .../FailIfChangelogNotSpecified.fsproj | 2 +- tests/fixtures/NuGet.config | 13 +++- ...ksForRelativePathWithKeepAChangelog.fsproj | 4 +- tests/fixtures/WorksForUnreleased.fsproj | 2 +- 6 files changed, 55 insertions(+), 38 deletions(-) diff --git a/tests/IntegrationTests.fs b/tests/IntegrationTests.fs index e162c47..24681a6 100644 --- a/tests/IntegrationTests.fs +++ b/tests/IntegrationTests.fs @@ -12,45 +12,47 @@ open Helpers module Utils = let packAndGetPackageProperties projectName = - let packageCache = VirtualWorkspace.``test-package-cache``.``.`` + task { - if Directory.Exists packageCache then - Directory.Delete(packageCache, true) + let packageCache = VirtualWorkspace.``test-package-cache``.``.`` - Directory.CreateDirectory packageCache |> ignore + // Force restoring of the latest package by clearing the local packages directory + if Directory.Exists packageCache then + Directory.Delete(packageCache, true) - Command.Run( - "dotnet", - CmdLine.empty - |> CmdLine.appendPrefix "restore" projectName - |> CmdLine.appendPrefix "--packages" VirtualWorkspace.``test-package-cache``.``.`` - |> CmdLine.toString, - workingDirectory = Workspace.fixtures.``.`` - ) + Directory.CreateDirectory packageCache |> ignore - Command.ReadAsync( - "dotnet", - CmdLine.empty - |> CmdLine.appendPrefix "pack" projectName - |> CmdLine.appendPrefix "-c" "Release" - |> CmdLine.append "--no-restore" - |> CmdLine.appendRaw "--getProperty:Version" - |> CmdLine.appendRaw "--getProperty:PackageVersion" - |> CmdLine.appendRaw "--getProperty:PackageReleaseNotes" - |> CmdLine.toString, - workingDirectory = Workspace.fixtures.``.`` - ) + // Read improves the error logging when the command fails + let! (_, _) = + Command.ReadAsync( + "dotnet", + CmdLine.empty + |> CmdLine.appendPrefix "restore" projectName + |> CmdLine.appendPrefix "--packages" VirtualWorkspace.``test-package-cache``.``.`` + |> CmdLine.toString, + workingDirectory = Workspace.fixtures.``.`` + ) + + return! + Command.ReadAsync( + "dotnet", + CmdLine.empty + |> CmdLine.appendPrefix "pack" projectName + |> CmdLine.appendPrefix "-c" "Release" + |> CmdLine.append "--no-restore" + |> CmdLine.appendRaw "--getProperty:Version" + |> CmdLine.appendRaw "--getProperty:PackageVersion" + |> CmdLine.appendRaw "--getProperty:PackageReleaseNotes" + |> CmdLine.toString, + workingDirectory = Workspace.fixtures.``.`` + ) + } [] type IntegrationTests() = - - member val testPackageVersion = null with get, set - - member this.AddPackageReference(projectName: string) = - let suffix = projectName.Replace(".fsproj", "") - - this.testPackageVersion <- $"0.0.1-test-{suffix}" - + [] + member this.Initialize() = + this.testPackageVersion <- $"0.0.1-test" // Create a package to be used in the tests // I didn't find a way to test the MSBuild tasks execution using MSBuild only // So each fsproj, will use a package reference to the package created here @@ -65,6 +67,9 @@ type IntegrationTests() = workingDirectory = Workspace.``..``.``.`` ) + member val testPackageVersion = null with get, set + + member this.AddPackageReference(projectName: string) = Command.Run( "dotnet", CmdLine.empty @@ -171,6 +176,7 @@ type IntegrationTests() = ) |> ignore } + [] member this.``generates a pre-release version if changelog has unreleased section``() : Task = task { diff --git a/tests/fixtures/FailIfChangelogDoesNotExist.fsproj b/tests/fixtures/FailIfChangelogDoesNotExist.fsproj index dd1973a..55da178 100644 --- a/tests/fixtures/FailIfChangelogDoesNotExist.fsproj +++ b/tests/fixtures/FailIfChangelogDoesNotExist.fsproj @@ -11,7 +11,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/fixtures/FailIfChangelogNotSpecified.fsproj b/tests/fixtures/FailIfChangelogNotSpecified.fsproj index b9a3b85..a088c30 100644 --- a/tests/fixtures/FailIfChangelogNotSpecified.fsproj +++ b/tests/fixtures/FailIfChangelogNotSpecified.fsproj @@ -7,7 +7,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/fixtures/NuGet.config b/tests/fixtures/NuGet.config index f7113cd..4049062 100644 --- a/tests/fixtures/NuGet.config +++ b/tests/fixtures/NuGet.config @@ -3,6 +3,17 @@ - + + + + + + + + + + + + diff --git a/tests/fixtures/WorksForRelativePathWithKeepAChangelog.fsproj b/tests/fixtures/WorksForRelativePathWithKeepAChangelog.fsproj index 7e033d0..a63abc6 100644 --- a/tests/fixtures/WorksForRelativePathWithKeepAChangelog.fsproj +++ b/tests/fixtures/WorksForRelativePathWithKeepAChangelog.fsproj @@ -10,8 +10,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/fixtures/WorksForUnreleased.fsproj b/tests/fixtures/WorksForUnreleased.fsproj index 2532691..20340e0 100644 --- a/tests/fixtures/WorksForUnreleased.fsproj +++ b/tests/fixtures/WorksForUnreleased.fsproj @@ -11,7 +11,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 0744fd3ac2d4e81ef0be9ce0c853dbe6d9953085 Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Sat, 26 Oct 2024 14:42:52 +0100 Subject: [PATCH 07/15] Add test for disabled pre-release --- tests/IntegrationTests.fs | 31 ++++++++++++++++++- ...ksForAbsolutePathWithKeepAChangelog.fsproj | 4 +-- 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/tests/IntegrationTests.fs b/tests/IntegrationTests.fs index 24681a6..ffa45d5 100644 --- a/tests/IntegrationTests.fs +++ b/tests/IntegrationTests.fs @@ -11,7 +11,7 @@ open Workspace open Helpers module Utils = - let packAndGetPackageProperties projectName = + let packAndGetPackagePropertiesWithExtraArg projectName (extraArg: string option) = task { let packageCache = VirtualWorkspace.``test-package-cache``.``.`` @@ -32,6 +32,8 @@ module Utils = |> CmdLine.toString, workingDirectory = Workspace.fixtures.``.`` ) + + let extraArg = extraArg |> Option.defaultValue "" return! Command.ReadAsync( @@ -40,6 +42,7 @@ module Utils = |> CmdLine.appendPrefix "pack" projectName |> CmdLine.appendPrefix "-c" "Release" |> CmdLine.append "--no-restore" + |> CmdLine.appendIfNotNullOrEmpty extraArg |> CmdLine.appendRaw "--getProperty:Version" |> CmdLine.appendRaw "--getProperty:PackageVersion" |> CmdLine.appendRaw "--getProperty:PackageReleaseNotes" @@ -47,6 +50,8 @@ module Utils = workingDirectory = Workspace.fixtures.``.`` ) } + + let packAndGetPackageProperties projectName = packAndGetPackagePropertiesWithExtraArg projectName None [] type IntegrationTests() = @@ -196,6 +201,30 @@ type IntegrationTests() = "PackageReleaseNotes": "### Removed\n\n- A test removal line\n- And another removal" } } +""" + ) + |> ignore + } + + [] + member this.``ignores a pre-release version if changelog has unreleased section but disabled``() : Task = + task { + let projectName = "WorksForUnreleased.fsproj" + + this.AddPackageReference projectName + + let! struct (stdout, _) = Utils.packAndGetPackagePropertiesWithExtraArg projectName (Some "-p:GenerateVersionForUnreleasedChanges=false") + + stdout + .Should() + .BeLineEndingEquivalent( + """{ + "Properties": { + "Version": "0.1.0", + "PackageVersion": "0.1.0", + "PackageReleaseNotes": "### Added\n\n- Created the package\n\n### Changed\n\n- Changed something in the package\n- Updated the target framework" + } +} """ ) |> ignore diff --git a/tests/fixtures/WorksForAbsolutePathWithKeepAChangelog.fsproj b/tests/fixtures/WorksForAbsolutePathWithKeepAChangelog.fsproj index 41ff699..51b0a62 100644 --- a/tests/fixtures/WorksForAbsolutePathWithKeepAChangelog.fsproj +++ b/tests/fixtures/WorksForAbsolutePathWithKeepAChangelog.fsproj @@ -10,8 +10,8 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all From 7ecbdbd5f0fff0498fa95e3833786ad126d32318 Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Sat, 26 Oct 2024 15:29:33 +0100 Subject: [PATCH 08/15] Share props with multiTargeting --- src/buildMultiTargeting/Ionide.KeepAChangelog.Tasks.props | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/buildMultiTargeting/Ionide.KeepAChangelog.Tasks.props diff --git a/src/buildMultiTargeting/Ionide.KeepAChangelog.Tasks.props b/src/buildMultiTargeting/Ionide.KeepAChangelog.Tasks.props new file mode 100644 index 0000000..cee0af5 --- /dev/null +++ b/src/buildMultiTargeting/Ionide.KeepAChangelog.Tasks.props @@ -0,0 +1,3 @@ + + + From 20a51eafcda31edb4e6dd52bfd4323e9cde4158d Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Sat, 26 Oct 2024 15:42:20 +0100 Subject: [PATCH 09/15] Update changelog --- CHANGELOG.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d48c6cc..75370f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,27 @@ # Changelog -## Unreleased +## [Unreleased] + +### Added + +* Warnings when the `` property is not set, or when the specified file cannot be found +* Automatic pre-release version bumping when an `Unreleased` section is found, which increments the patch version of the latest release and adds the "-alpha" suffix +* Added a property `` which can be set to `false` to prevent automatic version bumping +* Added an output property `UnreleasedReleaseNotes` containing the contents of the `Unreleased` section + +### Changed + +* No longer automatically use a `CHANGELOG.md` file, the path to the changelog must not be specified with the `` property +* Swapped to using the `KeepAChangelogParser` package for parsing ### Fixed * Fix bundled dependencies into the package for net6.0 +* Support for use in Visual Studio + +### Removed + +* Removed the Ionide.KeepAChangelog parser package ## [0.2.0] - 2023.12.05 From 1a48e7df8c1a44bd9df6f03951904b3cd52dd643 Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Sat, 26 Oct 2024 15:47:12 +0100 Subject: [PATCH 10/15] Copy readme notes from #5 --- README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 4566c2c..ae1b663 100644 --- a/README.md +++ b/README.md @@ -71,18 +71,25 @@ If your changelog has multiple versions, the latest one will be used. There's really only one property that matters for these targets, and that's `ChangelogFile`. This needs to point to the Changelog file you want to read, but it defaults to `CHANGELOG.md` in the root of a given project in case you want to adhere to defaults. + +| Property | Type | Default Value | Description | +| - | - | - | - | +| ChangelogFile | string | CHANGELOG.md | Points to the changelog file to parse. Note that the default value is set to the _project_ root by default, so a repository-wide changelog would require this property be set to a different value, for example in a Directory.Build.props file | +| GenerateVersionForUnreleasedChanges | boolean | true | If set, the assembly/package version and release notes will be set from Unreleased changes, if any are present. | + ## API When the task runs, it writes several output items and properties: |Name|Type|Description| |----|----|-----------| -| UnreleasedChangelog | UnreleasedChangelogData option | If present, there was an 'Unreleased' section in the Changelog. This structure will contain the sections present. | +| UnreleasedChangelog | ReleaseChangelogData option | If present, there was an 'Unreleased' section in the Changelog. This structure will contain the sections present, as well as an auto-incremented version number for this release. | +| UnreleasedReleaseNotes | string option | If present, contains the concatenated list of all Changelog sections for the Unreleased section of the Changelog. This is a convenience property so that you don't have to String.Join all the lines in the `ReleaseChangelogData` structure yourself! | | CurrentReleaseChangelog | ReleaseChangelogData option | If present, there was at least one released logged in the Changelog. This structure will contain the details of each one. | | AllReleasedChangelogs | ReleaseChangelogData list | Contains the ordered list of all released in the ChangelogFile, descending. | | LatestReleaseNotes | string option | If present, contains the concatenated list of all Changelog sections for the latest release. This is a convenience property so that you don't have to String.Join all the lines in the `ReleaseChangelogData` yourself! | -### ChangelogData +### ReleaseChangelogData This TaskItem has metadata for each of the known sections of a Changelog: @@ -95,14 +102,7 @@ This TaskItem has metadata for each of the known sections of a Changelog: In each case, the value of the metadata is the newline-concatenated list of all of the Changelog Entries for that section. -### UnreleasedChangelogData - -This structure is a `ChangelogData` with an `Identity` of `"Unreleased"`. - -### ReleaseChangelogData - -This structure is the same as `ChangelogData`, but it contains two more items of metadata: - +In addition, * the `Identity` of the `TaskItem` is the Semantic Version of the release * the `Date` of the `TaskItem` is the `YYYY-MM-DD`-formatted date of the release From e3a3d5521a07405cb7616262bb2d5ec4d118114a Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Sat, 26 Oct 2024 16:01:13 +0100 Subject: [PATCH 11/15] Update readme --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index ae1b663..c54cdc7 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -# Ionide.KeepAChangelog +# Ionide.KeepAChangelog.Tasks -This project implements a Changelog parser according to the spec at KeepAChangelog. It also provides MSBuild tasks and targets to automate the setting of **Versions** and **Package Release Notes** for your NuGet packages, so that the Changelogs are your source of truth. +This project provides MSBuild tasks and targets to automate the setting of **Versions** and **Package Release Notes** for your NuGet packages from changelogs meeting the spec at [KeepAChangelog](https://keepachangelog.com), so that the Changelogs are your source of truth. -When configured, this package will set the `Version`, `PackageVersion`, and `PackageReleaseNotes` of your packable project with the matching data from the latest Changelog release, as well as adding AssemblyMetadata for the `BuildDate` in the `YYYY-mm-dd` format. +When configured, this package will set the `Version`, `PackageVersion`, and `PackageReleaseNotes` of your packable project with the matching data from the latest Changelog release, or calculated from the unreleased section, as well as adding AssemblyMetadata for the `BuildDate` in the `YYYY-mm-dd` format. ## Installation @@ -69,13 +69,13 @@ If your changelog has multiple versions, the latest one will be used. ## Customization -There's really only one property that matters for these targets, and that's `ChangelogFile`. This needs to point to the Changelog file you want to read, but it defaults to `CHANGELOG.md` in the root of a given project in case you want to adhere to defaults. +There's really only one property that matters for these targets, and that's `ChangelogFile`. This needs to point to the Changelog file you want to read, and a warning will be emitted if you try to package your project without having set this. -| Property | Type | Default Value | Description | -| - | - | - | - | -| ChangelogFile | string | CHANGELOG.md | Points to the changelog file to parse. Note that the default value is set to the _project_ root by default, so a repository-wide changelog would require this property be set to a different value, for example in a Directory.Build.props file | -| GenerateVersionForUnreleasedChanges | boolean | true | If set, the assembly/package version and release notes will be set from Unreleased changes, if any are present. | +| Property | Type | Default Value | Description | +| - | - |---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ChangelogFile | string | - | Points to the changelog file to parse. Note that the value is relative to the _project_ root by default, so a repository-wide changelog would require this property be set to a different value, for example in a Directory.Build.props file | +| GenerateVersionForUnreleasedChanges | boolean | true | If set, the assembly/package version and release notes will be set from Unreleased changes, if any are present. | ## API @@ -86,7 +86,7 @@ When the task runs, it writes several output items and properties: | UnreleasedChangelog | ReleaseChangelogData option | If present, there was an 'Unreleased' section in the Changelog. This structure will contain the sections present, as well as an auto-incremented version number for this release. | | UnreleasedReleaseNotes | string option | If present, contains the concatenated list of all Changelog sections for the Unreleased section of the Changelog. This is a convenience property so that you don't have to String.Join all the lines in the `ReleaseChangelogData` structure yourself! | | CurrentReleaseChangelog | ReleaseChangelogData option | If present, there was at least one released logged in the Changelog. This structure will contain the details of each one. | -| AllReleasedChangelogs | ReleaseChangelogData list | Contains the ordered list of all released in the ChangelogFile, descending. | +| AllReleasedChangelogs | ReleaseChangelogData list | Contains the ordered list of all releases in the ChangelogFile, descending. | | LatestReleaseNotes | string option | If present, contains the concatenated list of all Changelog sections for the latest release. This is a convenience property so that you don't have to String.Join all the lines in the `ReleaseChangelogData` yourself! | ### ReleaseChangelogData From 3ad640c8c59763b90873b31bb88c247d1edb219c Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Sat, 26 Oct 2024 16:14:36 +0100 Subject: [PATCH 12/15] Format --- tests/IntegrationTests.fs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/IntegrationTests.fs b/tests/IntegrationTests.fs index ffa45d5..48e8030 100644 --- a/tests/IntegrationTests.fs +++ b/tests/IntegrationTests.fs @@ -32,7 +32,7 @@ module Utils = |> CmdLine.toString, workingDirectory = Workspace.fixtures.``.`` ) - + let extraArg = extraArg |> Option.defaultValue "" return! @@ -50,8 +50,9 @@ module Utils = workingDirectory = Workspace.fixtures.``.`` ) } - - let packAndGetPackageProperties projectName = packAndGetPackagePropertiesWithExtraArg projectName None + + let packAndGetPackageProperties projectName = + packAndGetPackagePropertiesWithExtraArg projectName None [] type IntegrationTests() = @@ -205,7 +206,7 @@ type IntegrationTests() = ) |> ignore } - + [] member this.``ignores a pre-release version if changelog has unreleased section but disabled``() : Task = task { @@ -213,7 +214,10 @@ type IntegrationTests() = this.AddPackageReference projectName - let! struct (stdout, _) = Utils.packAndGetPackagePropertiesWithExtraArg projectName (Some "-p:GenerateVersionForUnreleasedChanges=false") + let! struct (stdout, _) = + Utils.packAndGetPackagePropertiesWithExtraArg + projectName + (Some "-p:GenerateVersionForUnreleasedChanges=false") stdout .Should() From 3f952c3e80dbf1914f5ec9e41fa95fc5bd3ec4ac Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Sun, 27 Oct 2024 15:00:12 +0000 Subject: [PATCH 13/15] Add configuration for builddate attribute --- README.md | 1 + src/build/Core.targets | 9 ++--- src/build/Ionide.KeepAChangelog.Tasks.props | 2 ++ tests/IntegrationTests.fs | 39 ++++++++++++--------- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index c54cdc7..aff40d3 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ There's really only one property that matters for these targets, and that's `Cha | - | - |---------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ChangelogFile | string | - | Points to the changelog file to parse. Note that the value is relative to the _project_ root by default, so a repository-wide changelog would require this property be set to a different value, for example in a Directory.Build.props file | | GenerateVersionForUnreleasedChanges | boolean | true | If set, the assembly/package version and release notes will be set from Unreleased changes, if any are present. | +| GenerateAssemblyBuildDateAttribute | boolean | true | If set, an assembly metadata attribute named "BuildDate" will be generated with the date (YYYY-MM-DD) of the parsed release. | ## API diff --git a/src/build/Core.targets b/src/build/Core.targets index 578a5e2..456533b 100644 --- a/src/build/Core.targets +++ b/src/build/Core.targets @@ -25,7 +25,8 @@ - + %(CurrentReleaseChangelog.Identity) @@ -42,10 +43,10 @@ <_ReleaseDate>%(UnreleasedChangelog.Date) - - + + <_Parameter1>BuildDate - <_Parameter2>%(_ReleaseDate) + <_Parameter2>$(_ReleaseDate) diff --git a/src/build/Ionide.KeepAChangelog.Tasks.props b/src/build/Ionide.KeepAChangelog.Tasks.props index 355bdc0..38b8b1c 100644 --- a/src/build/Ionide.KeepAChangelog.Tasks.props +++ b/src/build/Ionide.KeepAChangelog.Tasks.props @@ -2,5 +2,7 @@ true + + true diff --git a/tests/IntegrationTests.fs b/tests/IntegrationTests.fs index 48e8030..ddcfc4b 100644 --- a/tests/IntegrationTests.fs +++ b/tests/IntegrationTests.fs @@ -75,24 +75,29 @@ type IntegrationTests() = member val testPackageVersion = null with get, set - member this.AddPackageReference(projectName: string) = - Command.Run( - "dotnet", - CmdLine.empty - |> CmdLine.appendPrefix "add" projectName - |> CmdLine.appendPrefix "package" "Ionide.KeepAChangelog.Tasks" - // |> CmdLine.appendPrefix "--source" VirtualWorkspace.``test-nupkgs``.``.`` - |> CmdLine.appendPrefix "--version" $"[{this.testPackageVersion}]" - |> CmdLine.toString, - workingDirectory = Workspace.fixtures.``.`` - ) + member this.AddPackageReference(projectName: string) : Task = + task { + let! struct (_, _) = + Command.ReadAsync( + "dotnet", + CmdLine.empty + |> CmdLine.appendPrefix "add" projectName + |> CmdLine.appendPrefix "package" "Ionide.KeepAChangelog.Tasks" + // |> CmdLine.appendPrefix "--source" VirtualWorkspace.``test-nupkgs``.``.`` + |> CmdLine.appendPrefix "--version" $"[{this.testPackageVersion}]" + |> CmdLine.toString, + workingDirectory = Workspace.fixtures.``.`` + ) + + () + } [] member this.``works for absolute path with keep a changelog``() : Task = task { let projectName = "WorksForAbsolutePathWithKeepAChangelog.fsproj" - this.AddPackageReference projectName + // this.AddPackageReference projectName let! struct (stdout, _) = Utils.packAndGetPackageProperties projectName @@ -116,7 +121,7 @@ type IntegrationTests() = task { let projectName = "WorksForRelativePathWithKeepAChangelog.fsproj" - this.AddPackageReference projectName + do! this.AddPackageReference projectName let! struct (stdout, _) = Utils.packAndGetPackageProperties projectName @@ -140,7 +145,7 @@ type IntegrationTests() = task { let projectName = "FailIfChangelogNotSpecified.fsproj" - this.AddPackageReference projectName + do! this.AddPackageReference projectName let! struct (stdout, _) = Utils.packAndGetPackageProperties projectName @@ -164,7 +169,7 @@ type IntegrationTests() = task { let projectName = "FailIfChangelogDoesNotExist.fsproj" - this.AddPackageReference projectName + do! this.AddPackageReference projectName let! struct (stdout, _) = Utils.packAndGetPackageProperties projectName @@ -188,7 +193,7 @@ type IntegrationTests() = task { let projectName = "WorksForUnreleased.fsproj" - this.AddPackageReference projectName + do! this.AddPackageReference projectName let! struct (stdout, _) = Utils.packAndGetPackageProperties projectName @@ -212,7 +217,7 @@ type IntegrationTests() = task { let projectName = "WorksForUnreleased.fsproj" - this.AddPackageReference projectName + do! this.AddPackageReference projectName let! struct (stdout, _) = Utils.packAndGetPackagePropertiesWithExtraArg From 393f135682b5f4f83eb6abd9739f0ae8d2909b9e Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Sun, 27 Oct 2024 16:55:42 +0000 Subject: [PATCH 14/15] Mention build date in changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 75370f5..f6f4bdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Automatic pre-release version bumping when an `Unreleased` section is found, which increments the patch version of the latest release and adds the "-alpha" suffix * Added a property `` which can be set to `false` to prevent automatic version bumping * Added an output property `UnreleasedReleaseNotes` containing the contents of the `Unreleased` section +* Added a property `` which controls whether the `BuildDate` `AssemblyAttribute` is written to the assembly, default `true` ### Changed From 12dbf85fdcd84c82825b0edfa20c89fe513d2ce5 Mon Sep 17 00:00:00 2001 From: Thomas Boby Date: Tue, 29 Oct 2024 08:11:50 +0000 Subject: [PATCH 15/15] Add tests for BuildDate --- Directory.Packages.props | 1 + tests/IntegrationTests.fs | 79 +++++++++++++++++-- tests/Ionide.KeepAChangelog.Tasks.Test.fsproj | 1 + tests/Workspace.fs | 3 + .../IgnoresBuildDateIfConfigured.fsproj | 22 ++++++ .../WorksForUnreleasedWhenIgnored.fsproj | 21 +++++ tests/packages.lock.json | 6 ++ 7 files changed, 125 insertions(+), 8 deletions(-) create mode 100644 tests/fixtures/IgnoresBuildDateIfConfigured.fsproj create mode 100644 tests/fixtures/WorksForUnreleasedWhenIgnored.fsproj diff --git a/Directory.Packages.props b/Directory.Packages.props index 03d8250..56d35f8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -21,5 +21,6 @@ + \ No newline at end of file diff --git a/tests/IntegrationTests.fs b/tests/IntegrationTests.fs index ddcfc4b..29f241c 100644 --- a/tests/IntegrationTests.fs +++ b/tests/IntegrationTests.fs @@ -1,6 +1,9 @@ module Tests.IntegrationTests open System.IO +open System.IO.Compression +open System.Reflection +open System.Runtime.InteropServices open System.Threading.Tasks open Ionide.KeepAChangelog.Tasks.Test open Microsoft.VisualStudio.TestTools.UnitTesting @@ -23,7 +26,7 @@ module Utils = Directory.CreateDirectory packageCache |> ignore // Read improves the error logging when the command fails - let! (_, _) = + let! _, _ = Command.ReadAsync( "dotnet", CmdLine.empty @@ -54,6 +57,32 @@ module Utils = let packAndGetPackageProperties projectName = packAndGetPackagePropertiesWithExtraArg projectName None + let getAssemblyInfoFromNupkg (projectName: string) version = + let projectName = Path.GetFileNameWithoutExtension projectName + + let packageFile = + Path.Combine(VirtualWorkspace.fixtures.bin.Release.``.``, $"{projectName}.{version}.nupkg") + + File.Exists(packageFile).Should().BeTrue() |> ignore + + use zip = ZipFile.OpenRead(packageFile) + use zipStream = zip.Entries |> Seq.find (_.Name.EndsWith(".dll")) |> _.Open() + use assemblyStream = new MemoryStream() + zipStream.CopyTo assemblyStream + + let runtimeAssemblies = + Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll") + + use mlc = new MetadataLoadContext(PathAssemblyResolver runtimeAssemblies) + let assembly = mlc.LoadFromStream assemblyStream + + assembly.CustomAttributes + |> Seq.tryPick (fun attr -> + match attr.ConstructorArguments |> Seq.map _.Value.ToString() |> Seq.toArray with + | [| "BuildDate"; date |] -> Some date + | _ -> None + ) + [] type IntegrationTests() = [] @@ -97,8 +126,6 @@ type IntegrationTests() = task { let projectName = "WorksForAbsolutePathWithKeepAChangelog.fsproj" - // this.AddPackageReference projectName - let! struct (stdout, _) = Utils.packAndGetPackageProperties projectName stdout @@ -114,6 +141,9 @@ type IntegrationTests() = """ ) |> ignore + + let buildDate = Utils.getAssemblyInfoFromNupkg projectName "0.1.0" + buildDate.Should().BeSome().WhoseValue.Should().Be("2022-01-13") |> ignore } [] @@ -125,6 +155,9 @@ type IntegrationTests() = let! struct (stdout, _) = Utils.packAndGetPackageProperties projectName + let buildDate = Utils.getAssemblyInfoFromNupkg projectName "0.1.0" + buildDate.Should().BeSome().WhoseValue.Should().Be("2022-01-13") |> ignore + stdout .Should() .BeLineEndingEquivalent( @@ -210,19 +243,46 @@ type IntegrationTests() = """ ) |> ignore + + let buildDate = Utils.getAssemblyInfoFromNupkg projectName "0.1.1-alpha" + buildDate.Should().BeNone() |> ignore } [] member this.``ignores a pre-release version if changelog has unreleased section but disabled``() : Task = task { - let projectName = "WorksForUnreleased.fsproj" + let projectName = "WorksForUnreleasedWhenIgnored.fsproj" + + do! this.AddPackageReference projectName + + let! struct (stdout, _) = Utils.packAndGetPackageProperties projectName + + let buildDate = Utils.getAssemblyInfoFromNupkg projectName "0.1.0" + buildDate.Should().BeSome().WhoseValue.Should().Be("2022-01-13") |> ignore + + stdout + .Should() + .BeLineEndingEquivalent( + """{ + "Properties": { + "Version": "0.1.0", + "PackageVersion": "0.1.0", + "PackageReleaseNotes": "### Added\n\n- Created the package\n\n### Changed\n\n- Changed something in the package\n- Updated the target framework" + } +} +""" + ) + |> ignore + } + + [] + member this.``doesn't write the build date if disabled``() : Task = + task { + let projectName = "IgnoresBuildDateIfConfigured.fsproj" do! this.AddPackageReference projectName - let! struct (stdout, _) = - Utils.packAndGetPackagePropertiesWithExtraArg - projectName - (Some "-p:GenerateVersionForUnreleasedChanges=false") + let! struct (stdout, _) = Utils.packAndGetPackageProperties projectName stdout .Should() @@ -237,4 +297,7 @@ type IntegrationTests() = """ ) |> ignore + + let buildDate = Utils.getAssemblyInfoFromNupkg projectName "0.1.0" + buildDate.Should().BeNone() |> ignore } diff --git a/tests/Ionide.KeepAChangelog.Tasks.Test.fsproj b/tests/Ionide.KeepAChangelog.Tasks.Test.fsproj index 6668db6..40e7a6d 100644 --- a/tests/Ionide.KeepAChangelog.Tasks.Test.fsproj +++ b/tests/Ionide.KeepAChangelog.Tasks.Test.fsproj @@ -25,6 +25,7 @@ + diff --git a/tests/Workspace.fs b/tests/Workspace.fs index af668b6..eb38323 100644 --- a/tests/Workspace.fs +++ b/tests/Workspace.fs @@ -10,5 +10,8 @@ type VirtualWorkspace = """ test-nupkgs/ test-package-cache/ +fixtures/ + bin/ + Release/ """ > diff --git a/tests/fixtures/IgnoresBuildDateIfConfigured.fsproj b/tests/fixtures/IgnoresBuildDateIfConfigured.fsproj new file mode 100644 index 0000000..7201cc9 --- /dev/null +++ b/tests/fixtures/IgnoresBuildDateIfConfigured.fsproj @@ -0,0 +1,22 @@ + + + + + net6.0 + + + + CHANGELOG_unreleased.md + false + false + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + diff --git a/tests/fixtures/WorksForUnreleasedWhenIgnored.fsproj b/tests/fixtures/WorksForUnreleasedWhenIgnored.fsproj new file mode 100644 index 0000000..b96c793 --- /dev/null +++ b/tests/fixtures/WorksForUnreleasedWhenIgnored.fsproj @@ -0,0 +1,21 @@ + + + + + net6.0 + + + + CHANGELOG_unreleased.md + false + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + diff --git a/tests/packages.lock.json b/tests/packages.lock.json index 0dcf2b7..441fd8c 100644 --- a/tests/packages.lock.json +++ b/tests/packages.lock.json @@ -111,6 +111,12 @@ "resolved": "12.0.0", "contentHash": "ptxlWtxC8vM6Y6e3h9ZTxBBkOWnWrm/Sa1HT+2i1xcXY3Hx2hmKDZP5RShPf8Xr9D+ivlrXNy57ktzyH8kyt+Q==" }, + "System.Reflection.MetadataLoadContext": { + "type": "Direct", + "requested": "[8.0.1, )", + "resolved": "8.0.1", + "contentHash": "c/hiLzoMeYWnoTsdeRY2o0Vbadfedt0b4ZawQ+qDr1BIoYBQo3ASei0Pyiz00n9pHBlfWXiUVy90tOWBEgJ8/Q==" + }, "Castle.Core": { "type": "Transitive", "resolved": "5.1.1",