diff --git a/Content/Library/.github/workflows/benchmark.yml b/Content/Library/.github/workflows/benchmark.yml
new file mode 100644
index 00000000..e839b58f
--- /dev/null
+++ b/Content/Library/.github/workflows/benchmark.yml
@@ -0,0 +1,40 @@
+name: Benchmark
+
+on:
+ push:
+ branches:
+ - MyReleaseBranch
+ pull_request:
+ branches:
+ - MyReleaseBranch
+
+jobs:
+ benchmark:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v5
+ - name: Setup necessary dotnet SDKs
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: |
+ 8.x
+ - name: Run benchmarks
+ run: |
+ chmod +x ./build.sh
+ ./build.sh RunBenchmarks
+ env:
+ CI: true
+ CONFIGURATION: Release
+ - name: Store benchmark result
+ uses: benchmark-action/github-action-benchmark@v1
+ with:
+ name: MyLib.1 Benchmark
+ tool: 'benchmarkdotnet'
+ output-file-path: 'benchmarks/**/BenchmarkDotNet.Artifacts/results/*.json'
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ auto-push: true
+ # Show alert with commit comment on detecting possible performance regression
+ alert-threshold: '200%'
+ comment-on-alert: true
+ fail-on-alert: false
+ alert-comment-cc-users: '@MyGithubUsername'
\ No newline at end of file
diff --git a/Content/Library/Directory.Packages.props b/Content/Library/Directory.Packages.props
index 0fe73715..138830ce 100644
--- a/Content/Library/Directory.Packages.props
+++ b/Content/Library/Directory.Packages.props
@@ -29,5 +29,6 @@
+
\ No newline at end of file
diff --git a/Content/Library/MyLib.1.sln b/Content/Library/MyLib.1.sln
index 5115b934..f5a61754 100644
--- a/Content/Library/MyLib.1.sln
+++ b/Content/Library/MyLib.1.sln
@@ -11,6 +11,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{ACBEE43C
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MyLib.1.Tests", "tests\MyLib.1.Tests\MyLib.1.Tests.fsproj", "{1CA2E092-2320-451D-A4F0-9ED7C7C528CA}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{B5B6CE6A-9B36-4F0F-8624-9B2CE14AC287}"
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "MyLib.1.Benchmarks", "benchmarks\MyLib.1.Benchmarks\MyLib.1.Benchmarks.fsproj", "{E6C4F8E5-9B5D-4A12-8F1E-2A9C7D3E4B6F}"
+EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "build", "build\build.fsproj", "{40D2259D-991D-44C4-B45D-C88CE0710C23}"
EndProject
Global
@@ -62,9 +66,22 @@ Global
{40D2259D-991D-44C4-B45D-C88CE0710C23}.Release|x64.Build.0 = Release|Any CPU
{40D2259D-991D-44C4-B45D-C88CE0710C23}.Release|x86.ActiveCfg = Release|Any CPU
{40D2259D-991D-44C4-B45D-C88CE0710C23}.Release|x86.Build.0 = Release|Any CPU
+ {E6C4F8E5-9B5D-4A12-8F1E-2A9C7D3E4B6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E6C4F8E5-9B5D-4A12-8F1E-2A9C7D3E4B6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E6C4F8E5-9B5D-4A12-8F1E-2A9C7D3E4B6F}.Debug|x64.ActiveCfg = Debug|x64
+ {E6C4F8E5-9B5D-4A12-8F1E-2A9C7D3E4B6F}.Debug|x64.Build.0 = Debug|x64
+ {E6C4F8E5-9B5D-4A12-8F1E-2A9C7D3E4B6F}.Debug|x86.ActiveCfg = Debug|x86
+ {E6C4F8E5-9B5D-4A12-8F1E-2A9C7D3E4B6F}.Debug|x86.Build.0 = Debug|x86
+ {E6C4F8E5-9B5D-4A12-8F1E-2A9C7D3E4B6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E6C4F8E5-9B5D-4A12-8F1E-2A9C7D3E4B6F}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E6C4F8E5-9B5D-4A12-8F1E-2A9C7D3E4B6F}.Release|x64.ActiveCfg = Release|x64
+ {E6C4F8E5-9B5D-4A12-8F1E-2A9C7D3E4B6F}.Release|x64.Build.0 = Release|x64
+ {E6C4F8E5-9B5D-4A12-8F1E-2A9C7D3E4B6F}.Release|x86.ActiveCfg = Release|x86
+ {E6C4F8E5-9B5D-4A12-8F1E-2A9C7D3E4B6F}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{5D30E174-2538-47AC-8443-318C8C5DC2C9} = {C397A34C-84F1-49E7-AEBC-2F9F2B196216}
{1CA2E092-2320-451D-A4F0-9ED7C7C528CA} = {ACBEE43C-7A88-4FB1-9B06-DB064D22B29F}
+ {E6C4F8E5-9B5D-4A12-8F1E-2A9C7D3E4B6F} = {B5B6CE6A-9B36-4F0F-8624-9B2CE14AC287}
EndGlobalSection
EndGlobal
diff --git a/Content/Library/README.md b/Content/Library/README.md
index ecd89172..b8e00862 100644
--- a/Content/Library/README.md
+++ b/Content/Library/README.md
@@ -76,6 +76,7 @@ src/MyLib.1/bin/
- `GenerateCoverageReport` - Code coverage is run during `DotnetTest` and this generates a report via [ReportGenerator](https://github.com/danielpalme/ReportGenerator).
- `ShowCoverageReport` - Shows the report generated in `GenerateCoverageReport`.
- `WatchTests` - Runs [dotnet watch](https://docs.microsoft.com/en-us/aspnet/core/tutorials/dotnet-watch?view=aspnetcore-3.0) with the test projects. Useful for rapid feedback loops.
+- `RunBenchmarks` - Runs [BenchmarkDotNet](https://benchmarkdotnet.org/) benchmarks in the `benchmarks` folder.
- `GenerateAssemblyInfo` - Generates [AssemblyInfo](https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualbasic.applicationservices.assemblyinfo?view=netframework-4.8) for libraries.
- `DotnetPack` - Runs [dotnet pack](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-pack). This includes running [Source Link](https://github.com/dotnet/sourcelink).
- `SourceLinkTest` - Runs a Source Link test tool to verify Source Links were properly generated.
diff --git a/Content/Library/benchmarks/Directory.Build.props b/Content/Library/benchmarks/Directory.Build.props
new file mode 100644
index 00000000..971b8f01
--- /dev/null
+++ b/Content/Library/benchmarks/Directory.Build.props
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/Content/Library/benchmarks/MyLib.1.Benchmarks/Library.Benchmarks.fs b/Content/Library/benchmarks/MyLib.1.Benchmarks/Library.Benchmarks.fs
new file mode 100644
index 00000000..31768413
--- /dev/null
+++ b/Content/Library/benchmarks/MyLib.1.Benchmarks/Library.Benchmarks.fs
@@ -0,0 +1,30 @@
+namespace MyLib._1.Benchmarks
+
+open BenchmarkDotNet.Attributes
+open MyLib._1
+open System
+
+[]
+type LibraryBenchmarks() =
+
+ let samplePerson = {
+ Say.Person.Name = "Benchmark"
+ Say.Person.FavoriteNumber = 42
+ Say.Person.FavoriteColor = Say.FavoriteColor.Blue
+ Say.Person.DateOfBirth = DateTimeOffset.Now
+ }
+
+ []
+ member _.HelloPerson() = Say.helloPerson samplePerson
+
+ []
+ member _.AddNumbers() = Say.add 100 200
+
+ []
+ member _.AddNumbersLoop() =
+ let mutable result = 0
+
+ for i in 1..1000 do
+ result <- Say.add result i
+
+ result
diff --git a/Content/Library/benchmarks/MyLib.1.Benchmarks/MyLib.1.Benchmarks.fsproj b/Content/Library/benchmarks/MyLib.1.Benchmarks/MyLib.1.Benchmarks.fsproj
new file mode 100644
index 00000000..f0dcb31b
--- /dev/null
+++ b/Content/Library/benchmarks/MyLib.1.Benchmarks/MyLib.1.Benchmarks.fsproj
@@ -0,0 +1,18 @@
+
+
+
+ Exe
+ net8.0
+ false
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Content/Library/benchmarks/MyLib.1.Benchmarks/Program.fs b/Content/Library/benchmarks/MyLib.1.Benchmarks/Program.fs
new file mode 100644
index 00000000..19b8c4b0
--- /dev/null
+++ b/Content/Library/benchmarks/MyLib.1.Benchmarks/Program.fs
@@ -0,0 +1,11 @@
+module Program
+
+open BenchmarkDotNet.Running
+open MyLib._1.Benchmarks
+
+[]
+let main args =
+ BenchmarkRunner.Run()
+ |> ignore
+
+ 0
diff --git a/Content/Library/build/build.fs b/Content/Library/build/build.fs
index b809220c..19e83d89 100644
--- a/Content/Library/build/build.fs
+++ b/Content/Library/build/build.fs
@@ -505,6 +505,23 @@ let watchTests _ =
cancelEvent.Cancel <- true
+let benchmarksGlob =
+ rootDirectory
+ > "benchmarks/**/*.??proj"
+
+let runBenchmarks _ =
+ !!benchmarksGlob
+ |> Seq.iter (fun proj ->
+ let result = DotNet.exec id "run" $"--project \"{proj}\" --configuration Release"
+
+ if
+ result.ExitCode
+ <> 0
+ then
+ Trace.traceError $"Warning: Benchmark failed for project %s{proj}"
+ Trace.traceError "This may be due to sandbox environment limitations"
+ )
+
let generateAssemblyInfo _ =
let (|Fsproj|Csproj|Vbproj|) (projFileName: string) =
@@ -727,6 +744,7 @@ let initTargets () =
Target.create "GenerateCoverageReport" generateCoverageReport
Target.create "ShowCoverageReport" showCoverageReport
Target.create "WatchTests" watchTests
+ Target.create "RunBenchmarks" runBenchmarks
Target.create "GenerateAssemblyInfo" generateAssemblyInfo
Target.create "DotnetPack" dotnetPack
Target.create "SourceLinkTest" sourceLinkTest
@@ -782,6 +800,9 @@ let initTargets () =
"DotnetBuild"
==>! "WatchDocs"
+ "DotnetBuild"
+ ==>! "RunBenchmarks"
+
"DotnetTest"
==> "GenerateCoverageReport"
==>! "ShowCoverageReport"
diff --git a/tests/MiniScaffold.Tests/Tests.fs b/tests/MiniScaffold.Tests/Tests.fs
index 371469f6..a39ea912 100755
--- a/tests/MiniScaffold.Tests/Tests.fs
+++ b/tests/MiniScaffold.Tests/Tests.fs
@@ -129,8 +129,15 @@ module Tests =
"-n MyCoolLib --githubUsername CoolPersonNo2",
[
yield! projectStructureAsserts
+ Assert.``File exists`` ".github/workflows/benchmark.yml"
+ Assert.``File exists``
+ "benchmarks/MyCoolLib.Benchmarks/MyCoolLib.Benchmarks.fsproj"
+ Assert.``File exists``
+ "benchmarks/MyCoolLib.Benchmarks/Library.Benchmarks.fs"
+ Assert.``File exists`` "benchmarks/MyCoolLib.Benchmarks/Program.fs"
Assert.``project can build target`` "DotnetPack"
Assert.``project can build target`` "BuildDocs"
+ Assert.``project can build target`` "RunBenchmarks"
]
@@ -187,6 +194,12 @@ module Tests =
"-n fsharp-data-sample --githubUsername CoolPersonNo2",
[
yield! projectStructureAsserts
+ Assert.``File exists`` ".github/workflows/benchmark.yml"
+ Assert.``File exists``
+ "benchmarks/fsharp-data-sample.Benchmarks/fsharp-data-sample.Benchmarks.fsproj"
+ Assert.``File exists``
+ "benchmarks/fsharp-data-sample.Benchmarks/Library.Benchmarks.fs"
+ Assert.``File exists`` "benchmarks/fsharp-data-sample.Benchmarks/Program.fs"
Assert.``project can build target`` "DotnetPack"
]
testCase,