Skip to content

Commit

Permalink
Add automated Versioning and release note creation (Issue #44).
Browse files Browse the repository at this point in the history
  • Loading branch information
Freymaurer committed Nov 19, 2020
1 parent 1765d9d commit 9a3ea60
Show file tree
Hide file tree
Showing 18 changed files with 602 additions and 184 deletions.
17 changes: 12 additions & 5 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
### v0.1.1 - 2020-11-18
* Rough feature set:
### 0.1.2+99a427 (Released 19.11.2020)
* Additions:
* #199a427
* Add automated Versioning and release note creation (Issue #44).
* Update Sql dump

### 0.1.1+7c567fd (Released 2020-11-18)
* Additions:
* #7c567fd
* Allow for multiples of the same column.
* Implement basic validation system for current worksheet. (WIP)
* Add info page with social media links and contact.
* Add extensive api docs.
* Bug fixes:
* Unit Term Search broke due to a change in naming conventions in the stored procedures. Fixed it!
* Bugfixes:
* Unit Term Search broke due to a change in naming conventions in the stored procedures. Fixed it!

### v0.1-beta - 2020-11-05
* Release of [Minimal POC milestone](https://github.com/nfdi4plants/Swate/milestone/1?closed=1). Rough feature set:
* Update advanced term search to use stored procedure introduced in 0.0.2-alpha.
* Bug fixes:
* Bugfixes:
* Responsive design should now render immediatly upon window size change
* Add-in should not reload after navigating to a new tab for the first time.
* Term search input field no longer looses focus after clicking into it.
Expand Down
276 changes: 264 additions & 12 deletions build.fsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ open Fake
open Fake.Core
open Fake.DotNet
open Fake.IO
open Fake.Tools.Git
open Farmer
open Farmer.Builders

Expand All @@ -21,8 +22,6 @@ let clientDeployPath = Path.combine clientPath "deploy"
let sharedTestsPath = Path.getFullName "./tests/Shared"
let serverTestsPath = Path.getFullName "./tests/Server"

let release = ReleaseNotes.load "RELEASE_NOTES.md"

let platformTool tool winTool =
let tool = if Environment.isUnix then tool else winTool
match ProcessUtils.tryFindFileOnPath tool with
Expand All @@ -39,6 +38,8 @@ let npmTool = platformTool "npm" "npm.cmd"
let npxTool = platformTool "npx" "npx.cmd"
let dockerComposeTool = platformTool "docker-compose" "docker-compose.exe"

let currentDateString = System.DateTime.Now.ToShortDateString()

let runTool cmd args workingDir =
let arguments = args |> String.split ' ' |> Arguments.OfArgs
Command.RawCommand (cmd, arguments)
Expand Down Expand Up @@ -93,6 +94,7 @@ Target.create "InstallClient" (fun _ ->
)

Target.create "Build" (fun _ ->
let release = ReleaseNotes.load "RELEASE_NOTES.md"
runDotNet "build" serverPath
Shell.regexReplaceInFileWithEncoding
"let app = \".+\""
Expand Down Expand Up @@ -149,7 +151,7 @@ Target.create "OfficeDebug" (fun _ ->
runTool dockerComposeTool "-f .db\docker-compose.yml up" __SOURCE_DIRECTORY__
}

let vsCodeSession = Environment.hasEnvironVar "vsCodeSession"
//let vsCodeSession = Environment.hasEnvironVar "vsCodeSession"
let safeClientOnly = Environment.hasEnvironVar "safeClientOnly"

let tasks =
Expand Down Expand Up @@ -245,18 +247,265 @@ Target.create "CreateDevCerts" (fun _ ->

let psi = new System.Diagnostics.ProcessStartInfo(FileName = certPath, UseShellExecute = true)
System.Diagnostics.Process.Start(psi) |> ignore

)

//Target.create "Bundle" (fun _ ->
// let serverDir = Path.combine deployDir "Server"
// let clientDir = Path.combine deployDir "Client"
// let publicDir = Path.combine clientDir "public"
// let publishArgs = sprintf "publish -c Release -o \"%s\"" serverDir
// runDotNet publishArgs serverPath
type SemVerRelease =
| Major
| Minor
| Patch
| WIP

type ReleaseNotesDescriptors =
| Additions
| Deletions
| Bugfixes

/// | Additions -> "Additions:" | Deletions -> "Deletions:" | Bugfixes -> "Bugfixes:"
member this.toString =
match this with
| Additions -> "Additions:"
| Deletions -> "Deletions:"
| Bugfixes -> "Bugfixes:"

static member DescriptorList =
[Additions.toString; Deletions.toString; Bugfixes.toString]

let createNewSemVer (semVerReleaseType:SemVerRelease) (newestCommitHash:string) (previousSemVer:SemVerInfo)=
match semVerReleaseType with
| Major ->
sprintf "%i.0.0+%s" (previousSemVer.Major+1u) newestCommitHash.[1..]
| Minor ->
sprintf "%i.%i.0+%s" (previousSemVer.Major) (previousSemVer.Minor+1u) newestCommitHash.[1..]
| Patch ->
sprintf "%i.%i.%i+%s" (previousSemVer.Major) (previousSemVer.Minor) (previousSemVer.Patch+1u) newestCommitHash.[1..]
| WIP ->
sprintf "%i.%i.%i+%s" (previousSemVer.Major) (previousSemVer.Minor) (previousSemVer.Patch) newestCommitHash.[1..]

// This is later used to try and sort the commit messages to the three fields additions, bugs and deletions.
let rec sortCommitsByKeyWords (all:string list) (additions:string list) (deletions:string list) (bugs:string list) =
let bugKeyWords = [|"bug"; "problem"|] |> Array.map String.toLower
let deleteKeyWords = [|"delete"; "remove"|] |> Array.map String.toLower
let isHeadBugKeyWord (head:string) = Array.exists (fun x -> x = head.ToLower()) bugKeyWords
let isHeadDeleteKeyWord (head:string) = Array.exists (fun x -> x = head.ToLower()) deleteKeyWords
match all with
| head::rest when isHeadBugKeyWord head
-> sortCommitsByKeyWords rest additions deletions (head::bugs)
| head::rest when isHeadDeleteKeyWord head
-> sortCommitsByKeyWords rest additions (head::deletions) bugs
| head::rest -> sortCommitsByKeyWords rest (head::additions) deletions bugs
| head::[] when isHeadBugKeyWord head
-> additions, deletions, (head::bugs)
| head::[] when isHeadDeleteKeyWord head
-> additions, (head::deletions), bugs
| head::[]
-> (head::additions), deletions, bugs
| []
-> additions, deletions, bugs
|> fun (x,y,z) -> List.rev x, List.rev y, List.rev z


let splitPreviousReleaseNotes releaseNotes =
let addOpt = releaseNotes |> List.tryFindIndex (fun x -> x = Additions.toString)
let deleteOpt = releaseNotes |> List.tryFindIndex (fun x -> x = Deletions.toString)
let bugOpt = releaseNotes |> List.tryFindIndex (fun x -> x = Bugfixes.toString)
let indList = [addOpt,Additions;deleteOpt,Deletions;bugOpt,Bugfixes] |> List.choose (fun (x,y) -> if x.IsSome then Some (x.Value, y) else None)
let addedDescriptors =
releaseNotes
|> List.mapi (fun i x ->
let descriptor = indList |> List.tryFindBack (fun (descInd,_) -> descInd <= i && ReleaseNotesDescriptors.DescriptorList |> List.contains x |> not)
if descriptor.IsNone then None else Some (snd descriptor.Value,x)
)
let findCommitsByDescriptor descriptor (commitOptionList:(ReleaseNotesDescriptors*string) option list) =
commitOptionList
|> List.choose (fun x ->
if x.IsSome && fst x.Value = descriptor then Some (snd x.Value) else None
)
|> List.map (fun x -> sprintf " * %s" x)
let prevAdditions =
findCommitsByDescriptor Additions addedDescriptors
// REMOVE this line as soon as parsing of semver metadata is fixed.
|> List.filter (fun x -> x.StartsWith " * #" |> not)
let prevDeletions = findCommitsByDescriptor Deletions addedDescriptors
let prevBugs = findCommitsByDescriptor Bugfixes addedDescriptors
prevAdditions, prevDeletions, prevBugs

Target.create "IsExistingReleaseNotes" (fun _ ->
let isExisting = Fake.IO.File.exists "RELEASE_NOTES.md"
if isExisting = false then
Fake.IO.File.create "RELEASE_NOTES.md"
Fake.IO.File.write
true
"RELEASE_NOTES.md"
[
sprintf "### 0.0.0 (Released %s)" (currentDateString)
"* Additions:"
" * Initial set up for RELEASE_Notes.md"
]
Trace.traceImportant "RELEASE_Notes.md created"
else
Trace.trace "RELEASE_Notes.md found"
)

Target.create "Release" (fun config ->

let semVer =
let opt =
config.Context.Arguments
|> List.tryFind (fun x -> x.StartsWith "semver:")
match opt with
| Some "semver:major"| Some "semver:Major" ->
Major
| Some "semver:minor"| Some "semver:Minor" ->
Minor
| Some "semver:Patch"| Some "semver:patch" ->
Patch
| Some "semver:wip"| Some "semver:WIP" ->
WIP
| Some x ->
Trace.traceError (sprintf "Unrecognized argument: \"%s\". Default to \"semver:wip\"." x)
WIP
| None -> WIP

let nOfLastCommitsToCheck =
let opt =
config.Context.Arguments
|> List.tryFind (fun x -> x.StartsWith "n:")
if opt.IsSome then opt.Value.Replace("n:","") else "30"

let prevReleaseNotes =
Fake.IO.File.read "RELEASE_NOTES.md"

let release = ReleaseNotes.load "RELEASE_NOTES.md"

Trace.trace (sprintf "%A" release.Notes)

// REMOVE this line as soon as parsing of semver metadata is fixed.
// This should be in release.SemVer.MetaData
let (tryFindPreviousReleaseCommitHash: string option) =
release.Notes
|> List.tryFind (fun x -> x.TrimStart([|' ';'*'|]).StartsWith "#")

if tryFindPreviousReleaseCommitHash.IsSome then
Trace.trace (sprintf "Found PreviousCommit: %s" tryFindPreviousReleaseCommitHash.Value)
else
Trace.traceError "Did not find previous Commit!"

//https://git-scm.com/book/en/v2/Git-Basics-Viewing-the-Commit-History#pretty_format
let allGitCommits =
Fake.Tools.Git.CommandHelper.runGitCommand "" ("log -" + nOfLastCommitsToCheck + " --pretty=format:\"%h;%s\"" )

let cutCommitsAtPreviousReleaseCommit =
allGitCommits
|> fun (_,gitCommits,_) ->
if tryFindPreviousReleaseCommitHash.IsSome then
let indOpt =
gitCommits |> List.tryFindIndex (fun y -> y.Contains tryFindPreviousReleaseCommitHash.Value.[1..])
let ind =
if indOpt.IsSome then
indOpt.Value
else
failwithf
"Could not find last version git hash: %s in the last %s commits.
You can increase the number of searched commits by passing a argument
as such \"dotnet fake build -t release n:50\""
tryFindPreviousReleaseCommitHash.Value nOfLastCommitsToCheck
gitCommits
|> List.take (ind)
else
gitCommits

Trace.trace "Update RELEASE_NOTES.md"

let writeNewReleaseNotes =

let commitNoteArr = cutCommitsAtPreviousReleaseCommit |> Array.ofList |> Array.map (fun x -> x.Split([|";"|],StringSplitOptions.None))
// REMOVE this line as soon as parsing of semver metadata is fixed.
// This should be in release.SemVer.MetaData
let latestCommitHash =
let newCommit = if tryFindPreviousReleaseCommitHash.IsSome then tryFindPreviousReleaseCommitHash.Value else ""
if Array.isEmpty commitNoteArr then newCommit else sprintf "#%s" commitNoteArr.[0].[0]
let newSemVer =
createNewSemVer semVer latestCommitHash.[1..] release.SemVer
/// This will be used to directly create the release notes
let formattedCommitNoteList =
commitNoteArr
|> Array.map (fun x ->
sprintf " * %s" x.[1]
)
|> List.ofArray
let additions, deletions, bugs = sortCommitsByKeyWords formattedCommitNoteList [] [] []

let newNotes =
if semVer <> WIP then
[
sprintf "### %s (Released %s)" newSemVer currentDateString
if List.isEmpty additions |> not then
"* Additions:"
// REMOVE this line as soon as parsing of semver metadata is fixed.
sprintf " * %s" latestCommitHash
yield! additions
if List.isEmpty deletions |> not then
"* Deletions:"
yield! deletions
if List.isEmpty bugs |> not then
"* Bugfixes:"
yield! bugs
""
yield! prevReleaseNotes
]
else
let prevAdditions, prevDeletions, prevBugs =
splitPreviousReleaseNotes release.Notes
let appendAdditions, appendDeletions, appendBugfixes =
additions@prevAdditions,deletions@prevDeletions,bugs@prevBugs
let skipPrevVersionOfReleaseNotes =
let findInd =
prevReleaseNotes
|> Seq.indexed
|> Seq.choose (fun (i,x) -> if x.StartsWith "###" then Some i else None)
|> Seq.skip 1
if Seq.isEmpty findInd then 0 else Seq.head findInd
[
sprintf "### %s (Released %s)" newSemVer currentDateString
if List.isEmpty appendAdditions |> not then
"* Additions:"
// REMOVE this line as soon as parsing of semver metadata is fixed.
sprintf " * %s" latestCommitHash
yield! appendAdditions
if List.isEmpty appendDeletions |> not then
"* Deletions:"
yield! appendDeletions
if List.isEmpty appendBugfixes |> not then
"* Bugfixes:"
yield! appendBugfixes
""
yield! (Seq.skip skipPrevVersionOfReleaseNotes prevReleaseNotes)
]


Fake.IO.File.write
false
"RELEASE_NOTES.md"
newNotes

writeNewReleaseNotes

Trace.trace "Update RELEASE_NOTES.md done!"

Trace.trace "Update Version.fs"

let releaseDate =
if release.Date.IsSome then release.Date.Value.ToShortDateString() else "WIP"

Fake.DotNet.AssemblyInfoFile.createFSharp "src/Server/Version.fs"
[ Fake.DotNet.AssemblyInfo.Title "SWATE"
Fake.DotNet.AssemblyInfo.Version release.AssemblyVersion
Fake.DotNet.AssemblyInfo.Metadata ("ReleaseDate",releaseDate)
//Fake.DotNet.AssemblyInfo.FileVersion release.AssemblyVersion
]

// Shell.copyDir publicDir clientDeployPath FileFilter.allFiles
//)
Trace.trace "Update Version.fs done!"
)

Target.create "Bundle" (fun _ ->
runDotNet (sprintf "publish -c Release -o \"%s\"" deployDir) serverPath
Expand Down Expand Up @@ -303,6 +552,9 @@ open Fake.Core.TargetOperators
==> "InstallClient"
==> "OfficeDebug"

"IsExistingReleaseNotes"
==> "Release"

"InstallOfficeAddinTooling"
==> "WebpackConfigSetup"
==> "LocalConnectionStringSetup"
Expand Down
2 changes: 2 additions & 0 deletions paket.dependencies
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ group Build
framework: netstandard2.0
storage: none

nuget Fake.DotNet.AssemblyInfoFile 5.20.3
nuget Fake.Tools.Git 5.20.3
nuget FSharp.Core
nuget Fake.Core.ReleaseNotes
nuget Fake.Core.Target
Expand Down
Loading

0 comments on commit 9a3ea60

Please sign in to comment.