Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

propose version bumps from unreleased changes #5

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
root = true

# All files
[*]
indent_style = space
trim_trailing_whitespace = true

# Xml files
[*.xml]
indent_size = 2

[.fs]
indent_size = 2
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ It might be helpful to see how this library can help you. Imagine you have a pr
and a CHANGELOG.md file like this:

```md
# Changelog
# Changelog

## 1.0.0 - 2022-01-14

Expand Down Expand Up @@ -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 |
| GenerateAssemblyBuildDateAttribute | boolean | true | If set, an assembly metadata attribute named "BuildDate" will be generated with the date (YYYY-MM-DD) of the parsed release. |
| 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! |
| 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` structure yourself! |

### ChangelogData
### ReleaseChangelogData

This TaskItem has metadata for each of the known sections of a Changelog:

Expand All @@ -95,13 +102,6 @@ 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
59 changes: 37 additions & 22 deletions src/Ionide.KeepAChangelog.Tasks/Library.fs
Original file line number Diff line number Diff line change
@@ -1,41 +1,37 @@
namespace KeepAChangelog.Tasks
namespace Ionide.KeepAChangelog.Tasks

open Microsoft.Build.Utilities
open Microsoft.Build.Framework
open System.IO
open Ionide.KeepAChangelog
open Ionide.KeepAChangelog.Domain
open System.Linq
open SemVersion
open System

module Util =
let mapReleaseInfo (version: SemVersion.SemanticVersion) (date: System.DateTime) (item: ITaskItem) : ITaskItem =
let mapReleaseInfo (version: SemanticVersion) (date: DateTime) (item: ITaskItem) : ITaskItem =
item.ItemSpec <- string version
item.SetMetadata("Date", date.ToString("yyyy-MM-dd"))
item

let mapUnreleasedInfo (item: ITaskItem) : ITaskItem =
item.ItemSpec <- "Unreleased"
item

let allReleaseNotesFor (data: ChangelogData) =
let section name items =
match items with
| [] -> []
| items -> $"### {name}" :: items @ [ "" ]

String.concat
System.Environment.NewLine
Environment.NewLine
([ yield! section "Added" data.Added
yield! section "Changed" data.Changed
yield! section "Deprecated" data.Deprecated
yield! section "Removed" data.Removed
yield! section "Fixed" data.Fixed
yield! section "Security" data.Security
for KeyValue(heading, lines) in data.Custom do
yield! section heading lines ])
for KeyValue (heading, lines) in data.Custom do
yield! section heading lines ])

let stitch items =
String.concat System.Environment.NewLine items
let stitch items = String.concat Environment.NewLine items

let mapChangelogData (data: ChangelogData) (item: ITaskItem) : ITaskItem =
item.SetMetadata("Added", stitch data.Added)
Expand All @@ -44,10 +40,27 @@ module Util =
item.SetMetadata("Removed", stitch data.Removed)
item.SetMetadata("Fixed", stitch data.Fixed)
item.SetMetadata("Security", stitch data.Security)
for (KeyValue(heading, lines)) in data.Custom do

for (KeyValue (heading, lines)) in data.Custom do
item.SetMetadata(heading, stitch lines)

item

let mapUnreleasedInfo changelogs (item: ITaskItem) : ITaskItem =
match Promote.fromUnreleased changelogs with
| None ->
item.ItemSpec <- "Unreleased"

changelogs.Unreleased
|> Option.map (fun d -> mapChangelogData d item)
|> Option.defaultValue item
| Some (unreleasedVersion, releaseDate, data) ->
let item = mapReleaseInfo unreleasedVersion releaseDate item

data
|> Option.map (fun d -> mapChangelogData d item)
|> Option.defaultValue item

type ParseChangelogs() =
inherit Task()

Expand All @@ -57,6 +70,9 @@ type ParseChangelogs() =
[<Output>]
member val UnreleasedChangelog: ITaskItem = null with get, set

[<Output>]
member val UnreleasedReleaseNotes: string = null with get, set

[<Output>]
member val CurrentReleaseChangelog: ITaskItem = null with get, set

Expand All @@ -66,6 +82,7 @@ type ParseChangelogs() =
[<Output>]
member val LatestReleaseNotes: string = null with get, set


override this.Execute() : bool =
let file = this.ChangelogFile |> FileInfo

Expand All @@ -77,10 +94,8 @@ type ParseChangelogs() =
| Ok changelogs ->
changelogs.Unreleased
|> Option.iter (fun unreleased ->
this.UnreleasedChangelog <-
TaskItem()
|> Util.mapChangelogData unreleased
|> Util.mapUnreleasedInfo)
this.UnreleasedChangelog <- TaskItem() |> Util.mapUnreleasedInfo changelogs
this.UnreleasedReleaseNotes <- Util.allReleaseNotesFor unreleased)

let sortedReleases =
// have to use LINQ here because List.sortBy* require IComparable, which
Expand All @@ -92,8 +107,10 @@ type ParseChangelogs() =
|> Seq.map (fun (version, date, data) ->
TaskItem()
|> Util.mapReleaseInfo version date
|> fun d -> match data with Some data -> Util.mapChangelogData data d | None -> d
)
|> fun d ->
match data with
| Some data -> Util.mapChangelogData data d
| None -> d)
|> Seq.toArray

this.AllReleasedChangelogs <- items
Expand All @@ -103,9 +120,7 @@ type ParseChangelogs() =
|> Seq.tryHead
|> Option.iter (fun (version, date, data) ->
data
|> Option.iter (fun data ->
this.LatestReleaseNotes <- Util.allReleaseNotesFor data)
)
|> Option.iter (fun data -> this.LatestReleaseNotes <- Util.allReleaseNotesFor data))

true
| Error (formatted, msg) ->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- Points to the location of the file used to determine the assembly/package version and release notes -->
<ChangelogFile Condition="'$(ChangelogFile)' == ''">CHANGELOG.md</ChangelogFile>
<!-- If true, assembly metadata for the build date of the release will be written -->
<GenerateAssemblyBuildDateAttribute Condition="'$(GenerateAssemblyBuildDateAttribute)' == ''">true</GenerateAssemblyBuildDateAttribute>
<!-- If set, the assembly/package version and release notes will be set from Unreleased changes, if any are present -->
<GenerateVersionForUnreleasedChanges Condition="'$(GenerateVersionForUnreleasedChanges)' == ''">true</GenerateVersionForUnreleasedChanges>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@
$(PrepareForBuildDependsOn)
</PrepareForBuildDependsOn>
</PropertyGroup>

<Target Name="GetChangelogVersion"
Condition="'$(ChangelogFile)' != '' and Exists('$(ChangelogFile)')"
Inputs="$(ChangelogFile)"
Outputs="UnreleasedChangelog;CurrentReleaseChangelog;AllReleasedChangelogslLatestReleaseNotes">
<Ionide.KeepAChangelog.Tasks.ParseChangeLogs
<Ionide.KeepAChangelog.Tasks.ParseChangeLogs
ChangelogFile="$(ChangelogFile)">
<Output TaskParameter="UnreleasedChangelog" ItemName="UnreleasedChangelog"/>
<Output TaskParameter="UnreleasedReleaseNotes" ItemName="UnreleasedReleaseNotes"/>
<Output TaskParameter="CurrentReleaseChangelog" ItemName="CurrentReleaseChangelog"/>
<Output TaskParameter="AllReleasedChangelogs" ItemName="AllReleasedChangelogs" />
<Output TaskParameter="LatestReleaseNotes" ItemName="LatestReleaseNotes" />
Expand All @@ -24,15 +25,24 @@
<Target Name="SetVersionFromChangelog"
DependsOnTargets="GetChangelogVersion">
<PropertyGroup Condition="'@(CurrentReleaseChangelog)' != ''">
<!-- Set the version to the version of the latest release -->
<Version>%(CurrentReleaseChangelog.Identity)</Version>
<PackageVersion>%(CurrentReleaseChangelog.Identity)</PackageVersion>
<PackageReleaseNotes>@(LatestReleaseNotes)</PackageReleaseNotes>
<_ReleaseDate>%(CurrentReleaseChangelog.Date)</_ReleaseDate>
</PropertyGroup>
<PropertyGroup Condition="'@(UnreleasedChangelog)' != '' and '$(GenerateVersionForUnreleasedChanges)' == 'true'">
<!-- Set the version to the derived version of the unreleased changelog -->
<Version>%(UnreleasedChangelog.Identity)</Version>
<PackageVersion>%(UnreleasedChangelog.Identity)</PackageVersion>
<PackageReleaseNotes>@(UnreleasedReleaseNotes)</PackageReleaseNotes>
<_ReleaseDate>%(UnreleasedChangelog.Date)</_ReleaseDate>
</PropertyGroup>

<ItemGroup Condition="'@(CurrentReleaseChangelog)' != '' and '$(GenerateAssemblyInfo)' == 'true'">
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute" Condition="'$(GenerateRepositoryUrlAttribute)' == 'true' and ('$(RepositoryUrl)' != '' or '$(PublishRepositoryUrl)' == 'true')" >
<ItemGroup Condition="'@(_ReleaseDate)' != '' and '$(GenerateAssemblyInfo)' == 'true'">
<AssemblyAttribute Include="System.Reflection.AssemblyMetadataAttribute" Condition="'$(GenerateAssemblyBuildDateAttribute)' == 'true'" >
<_Parameter1>BuildDate</_Parameter1>
<_Parameter2>%(CurrentReleaseChangelog.Date)</_Parameter2>
<_Parameter2>@(_ReleaseDate)</_Parameter2>
</AssemblyAttribute>
</ItemGroup>
</Target>
Expand Down
Loading