diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 5424e2e..674865a 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -2,18 +2,19 @@ name: CI Build run-name: CIBuild_${{ github.event_name }}_${{ github.ref_name }}_${{ github.run_number }}.${{ github.run_attempt }} env: - PKG_MAJOR_VERSION: 1.2 + PKG_MAJOR_VERSION: 1.3 PROJECT_NAME: DNX.Extensions DOTNET_VERSION: 8.0.x NUGET_VERSION: 5.x BUILD_CONFIG: Release BUILD_PLATFORM: Any CPU PACK_PARAMETERS: '' - COVERAGE_WARNING_THRESHOLD: 60 - COVERAGE_ERROR_THRESHOLD: 80 + COVERAGE_WARNING_THRESHOLD: 95 + COVERAGE_ERROR_THRESHOLD: 85 NUGET_OUTPUT_FOLDER: nupkgs BRANCH_PREFIX_RELEASE_CANDIDATE: rc/ BRANCH_PREFIX_PUBLISH_CANDIDATE: beta/ + BRANCH_NAME_BENCHMARK_CANDIDATE: benchmark on: push: @@ -40,39 +41,79 @@ jobs: steps: - name: Get Current Build Date - id: build_date run: echo "build_date=$(date +'%y%j')" >> $GITHUB_ENV - - name: Set Package Suffix - id: package_suffix + - name: Evaluate pipeline conditions run: | - branch="${{ github.ref }}" - package_suffix='' + is_primary_branch=false + is_pull_request_build=false + is_release_candidate_branch=false + is_publish_candidate_branch=false + is_benchmark_candidate_branch=false - if [ "$branch" != "refs/heads/main" ] + # Primary Branch ? + if [ "${{ github.ref }}" == 'refs/heads/main' ] then - package_suffix='-beta' + is_primary_branch=true fi - echo "package_suffix=${package_suffix}" >> $GITHUB_ENV + # Pull Request ? + if [ "${{ github.event_name }}" == "pull_request" ] + then + is_pull_request_build=true + fi + + # If Release Candidate branch ? + if [[ "${{ github.ref }}" == refs/heads/${{ env.BRANCH_PREFIX_RELEASE_CANDIDATE }}* ]] + then + is_release_candidate_branch=true + fi + + # Is Publish Candidate branch ? + if [[ "${{ github.ref }}" == refs/heads/${{ env.BRANCH_PREFIX_PUBLISH_CANDIDATE }}* ]] + then + is_publish_candidate_branch=true + fi + + # Is Benchmark Candidate branch ? + if [[ "${{ github.ref }}" == *${{ env.BRANCH_NAME_BENCHMARK_CANDIDATE }}* ]] + then + is_benchmark_candidate_branch=true + fi + + # Set for later steps + echo "is_primary_branch=${is_primary_branch}" >> $GITHUB_ENV + echo "is_pull_request_build=${is_pull_request_build}" >> $GITHUB_ENV + echo "is_release_candidate_branch=${is_release_candidate_branch}" >> $GITHUB_ENV + echo "is_publish_candidate_branch=${is_publish_candidate_branch}" >> $GITHUB_ENV + echo "is_benchmark_candidate_branch=${is_benchmark_candidate_branch}" >> $GITHUB_ENV + + - name: Determine Tagging + run: | + should_tag=false + + if $is_primary_branch + then + should_tag=true + fi + + echo "should_tag=${should_tag}" >> $GITHUB_ENV - - name: Determing GitHub Releasing - id: should_release + - name: Determine GitHub Releasing run: | should_release=false release_is_draft=false release_is_prerelease=false - if [ "${{ github.ref }}" == 'refs/heads/main' ] + if $is_primary_branch then should_release=true - elif [[ "${{ github.ref }}" == refs/heads/${{ env.BRANCH_PREFIX_RELEASE_CANDIDATE }}* ]] + elif $is_release_candidate_branch then should_release=true release_is_draft=true - if [ "${{ github.event_name }}" == "pull_request" ] - then + if $is_pull_request_build; then release_is_draft=false release_is_prerelease=true fi @@ -82,49 +123,80 @@ jobs: echo "release_is_draft=${release_is_draft}" >> $GITHUB_ENV echo "release_is_prerelease=${release_is_prerelease}" >> $GITHUB_ENV + - name: Determine Benchmarking + run: | + should_benchmark=false + + if $should_release + then + should_benchmark=true + elif $is_pull_request_build + then + should_benchmark=true + elif $is_benchmark_candidate_branch + then + should_benchmark=true + fi + + echo "should_benchmark=${should_benchmark}" >> $GITHUB_ENV + - name: Determine package publishing - id: should_publish run: | should_publish=false - if [ "${{ github.event_name }}" == "pull_request" ] + if $is_primary_branch then should_publish=true - elif [ "${{ github.ref }}" == "refs/heads/main" ] + elif $is_pull_request_build then should_publish=true - elif [[ "${{ github.ref }}" == refs/heads/${{ env.BRANCH_PREFIX_PUBLISH_CANDIDATE }}* ]] + elif $is_publish_candidate_branch then should_publish=true fi echo "should_publish=${should_publish}" >> $GITHUB_ENV + - name: Set Package Suffix + run: | + package_suffix='' + + if !($is_primary_branch) + then + package_suffix='-beta' + + if $should_release + then + package_suffix='-rc' + fi + fi + + echo "package_suffix=${package_suffix}" >> $GITHUB_ENV + - name: Set Product Version - id: product_version run: echo "product_version=${{ env.PKG_MAJOR_VERSION }}" >> $GITHUB_ENV - name: Set Assembly Version - id: assembly_version run: echo "assembly_version=${{ env.PKG_MAJOR_VERSION }}.${{ env.build_date }}.${{ github.run_number }}${{ github.run_attempt }}" >> $GITHUB_ENV - name: Set Package Version - id: package_version run: echo "package_version=${{ env.assembly_version }}${{ env.package_suffix }}" >> $GITHUB_ENV - name: Show Configuration - id: show_configuration run: env | sort outputs: assembly_version: ${{ env.assembly_version }} product_version: ${{ env.product_version }} package_version: ${{ env.package_version }} + should_tag: ${{ env.should_tag }} should_publish: ${{ env.should_publish }} should_release: ${{ env.should_release }} + should_benchmark: ${{ env.should_benchmark }} release_is_draft: ${{ env.release_is_draft }} release_is_prerelease: ${{ env.release_is_prerelease }} + ########################################################## ## Build DotNet projects build: @@ -160,8 +232,6 @@ jobs: comment-title: 'Unit Test Results' results-path: "**/*.trx" coverage-type: cobertura - #coverage-path: "**/coverage.cobertura.xml" - #coverage-threshold: ${{ env.COVERAGE_WARNING_THRESHOLD }} - name: Code Coverage Report uses: irongut/CodeCoverageSummary@v1.3.0 @@ -204,8 +274,32 @@ jobs: path: "${{ env.NUGET_OUTPUT_FOLDER }}/**" if-no-files-found: error + + ########################################################## + ## Tag in git + tag: + name: Tag in GitHub + if: needs.setup.outputs.should_tag == 'true' + + needs: + - setup + - build + + runs-on: ubuntu-latest + + steps: + - name: Tag git + uses: pkgdeps/git-tag-action@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + github_repo: ${{ github.repository }} + git_commit_sha: ${{ github.sha }} + git_tag_prefix: "v" + version: ${{ needs.setup.outputs.assembly_version }} + + ########################################################## - ## Generate a Release and Tag in git + ## Generate a GitHub Release release: name: Create GitHub Release if: needs.setup.outputs.should_release == 'true' @@ -226,7 +320,6 @@ jobs: path: nuget - name: Build Changelog - id: build_changelog uses: mikepenz/release-changelog-builder-action@v5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -242,14 +335,35 @@ jobs: removeArtifacts: true artifacts: '**/*.nupkg' - - name: Tag git - uses: pkgdeps/git-tag-action@v3 + + ########################################################## + ## Benchmark Performance + benchmark: + name: Benchmark Performance + if: needs.setup.outputs.should_benchmark == 'true' + + needs: + - setup + - build + + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install .NET SDK + uses: actions/setup-dotnet@v4 with: - github_token: ${{ secrets.GITHUB_TOKEN }} - github_repo: ${{ github.repository }} - git_commit_sha: ${{ github.sha }} - git_tag_prefix: "v" - version: ${{ needs.setup.outputs.assembly_version }} + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: Run Benchmarks + run: dotnet run --configuration ${{ env.BUILD_CONFIG }} --project tests/DNX.Extensions.Benchmarks + + - name: Publish Benchmark output + run: | + cat BenchmarkDotNet.Artifacts/results/*Benchmarks-report-github.md >> $GITHUB_STEP_SUMMARY + ########################################################## ## Publish to NuGet @@ -271,7 +385,6 @@ jobs: nuget-version: ${{ env.NUGET_VERSION }} - name: Download NuGet Output - id: download_nuget uses: actions/download-artifact@v4 with: name: nuget_output diff --git a/DNX.Extensions.sln b/DNX.Extensions.sln index dd9ab94..476873c 100644 --- a/DNX.Extensions.sln +++ b/DNX.Extensions.sln @@ -24,8 +24,12 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".docs", ".docs", "{8700E3D9-9248-4679-A0E8-8AB3E0B3B09D}" ProjectSection(SolutionItems) = preProject LICENSE = LICENSE + README.md = README.md + To Do.md = To Do.md EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DNX.Extensions.Benchmarks", "tests\DNX.Extensions.Benchmarks\DNX.Extensions.Benchmarks.csproj", "{D9664527-B415-498F-B8A9-9AF016D7D91F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -40,6 +44,10 @@ Global {B14EFA70-CA77-4439-9C08-357061B97E4D}.Debug|Any CPU.Build.0 = Debug|Any CPU {B14EFA70-CA77-4439-9C08-357061B97E4D}.Release|Any CPU.ActiveCfg = Release|Any CPU {B14EFA70-CA77-4439-9C08-357061B97E4D}.Release|Any CPU.Build.0 = Release|Any CPU + {D9664527-B415-498F-B8A9-9AF016D7D91F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D9664527-B415-498F-B8A9-9AF016D7D91F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D9664527-B415-498F-B8A9-9AF016D7D91F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D9664527-B415-498F-B8A9-9AF016D7D91F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -47,6 +55,7 @@ Global GlobalSection(NestedProjects) = preSolution {E2B8F640-BCFE-4BD7-B158-E7FE957A38CB} = {E832563B-3DD6-4CCD-B5DA-91F89AE3B98A} {B14EFA70-CA77-4439-9C08-357061B97E4D} = {27BBD260-1C33-4F57-925A-12AB922D6AB5} + {D9664527-B415-498F-B8A9-9AF016D7D91F} = {27BBD260-1C33-4F57-925A-12AB922D6AB5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A971C3D4-3729-43C6-88E7-16371F03161F} diff --git a/To Do.md b/To Do.md new file mode 100644 index 0000000..d8ccd16 --- /dev/null +++ b/To Do.md @@ -0,0 +1,20 @@ +# TO DO + +## Variables with multiple values + +- https://github.com/orgs/community/discussions/46785 + +## Multi Targeting + +- https://learn.microsoft.com/en-us/answers/questions/1398343/nuget-how-can-i-make-a-single-nuget-package-target +- https://runs-on.com/github-actions/the-matrix-strategy/ +- https://github.com/NuGet/setup-nuget + +## Better Test Reporting ? + +- https://github.com/marketplace/actions/dotnet-test-reporter + +## NuGet signing + +- https://timheuer.com/blog/use-nuget-with-github-actions-github-packages/ +- diff --git a/src/DNX.Extensions/DNX.Extensions.csproj b/src/DNX.Extensions/DNX.Extensions.csproj index c1ca19a..44008b7 100644 --- a/src/DNX.Extensions/DNX.Extensions.csproj +++ b/src/DNX.Extensions/DNX.Extensions.csproj @@ -6,6 +6,7 @@ x64;x86;AnyCPU true disable + true @@ -19,11 +20,15 @@ favicon-32x32.png https://raw.githubusercontent.com/martinsmith1968/DNX.Extensions/main/images/favicon-32x32.png - Interpolation to a working version and some preparation for moving to .NET Standard + Migration from DNX.Helpers + + True + + True diff --git a/src/DNX.Extensions/IO/DirectoryInfoExtensions.cs b/src/DNX.Extensions/IO/DirectoryInfoExtensions.cs index ce2b1e5..34cab8b 100644 --- a/src/DNX.Extensions/IO/DirectoryInfoExtensions.cs +++ b/src/DNX.Extensions/IO/DirectoryInfoExtensions.cs @@ -94,10 +94,12 @@ public static string GetRelativePath(this DirectoryInfo directoryInfo, Directory : GetRelativePath(directoryInfo.FullName, owningDirectoryInfo.FullName) .RemoveStartsWith($".{Path.DirectorySeparatorChar}"); +#if NETSTANDARD2_1_OR_GREATER if (relativePath == ".") { relativePath = string.Empty; } +#endif return relativePath; } diff --git a/tests/DNX.Extensions.Benchmarks/DNX.Extensions.Benchmarks.csproj b/tests/DNX.Extensions.Benchmarks/DNX.Extensions.Benchmarks.csproj new file mode 100644 index 0000000..fe5b61f --- /dev/null +++ b/tests/DNX.Extensions.Benchmarks/DNX.Extensions.Benchmarks.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + enable + enable + false + + + + + + + + + + + diff --git a/tests/DNX.Extensions.Benchmarks/DevelopmentConfig.cs b/tests/DNX.Extensions.Benchmarks/DevelopmentConfig.cs new file mode 100644 index 0000000..c78f585 --- /dev/null +++ b/tests/DNX.Extensions.Benchmarks/DevelopmentConfig.cs @@ -0,0 +1,20 @@ +using System.Diagnostics.CodeAnalysis; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Exporters; + +namespace DNX.Extensions.Benchmarks; + +[ExcludeFromCodeCoverage] +internal class DevelopmentConfig : ManualConfig +{ + public DevelopmentConfig() + { + AddExporter(DefaultConfig.Instance.GetExporters().ToArray()); + AddLogger(DefaultConfig.Instance.GetLoggers().ToArray()); + AddColumnProvider(DefaultConfig.Instance.GetColumnProviders().ToArray()); + + AddExporter(new HtmlExporter()); + + WithOptions(ConfigOptions.DisableOptimizationsValidator); + } +} diff --git a/tests/DNX.Extensions.Benchmarks/Program.cs b/tests/DNX.Extensions.Benchmarks/Program.cs new file mode 100644 index 0000000..1c8dac1 --- /dev/null +++ b/tests/DNX.Extensions.Benchmarks/Program.cs @@ -0,0 +1,21 @@ +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using BenchmarkDotNet.Running; + +namespace DNX.Extensions.Benchmarks; + +[ExcludeFromCodeCoverage] +public class Program +{ + public static void Main() + { + BenchmarkRunner.Run( + Assembly.GetExecutingAssembly(), +#if DEBUG + new DevelopmentConfig() +#else + null +#endif + ); + } +} diff --git a/tests/DNX.Extensions.Benchmarks/Strings/StringExtensionBenchmarks.cs b/tests/DNX.Extensions.Benchmarks/Strings/StringExtensionBenchmarks.cs new file mode 100644 index 0000000..312ee2d --- /dev/null +++ b/tests/DNX.Extensions.Benchmarks/Strings/StringExtensionBenchmarks.cs @@ -0,0 +1,41 @@ +using System.Diagnostics.CodeAnalysis; +using BenchmarkDotNet.Attributes; +using DNX.Extensions.Strings; + +namespace DNX.Extensions.Benchmarks.Strings; + +[ExcludeFromCodeCoverage] +[MemoryDiagnoser] +[MarkdownExporterAttribute.GitHub] +public class StringExtensionBenchmarks +{ + private const string SearchText = nameof(SearchText); + + private string _text = ""; + + [Params(100)] + public int StartTextSize { get; set; } + + [Params(100)] + public int EndTextSize { get; set; } + + [GlobalSetup] + public void GlobalSetup() + { + _text = string.Format( + "{0}{1}{2}", + new string('-', StartTextSize), + SearchText, + new string('-', EndTextSize) + ); + } + + [Benchmark] + public string Before() => _text.Before(SearchText); + + [Benchmark] + public string After() => _text.After(SearchText); + + [Benchmark] + public string Reverse() => _text.Reverse(); +} diff --git a/tests/DNX.Extensions.Tests/DNX.Extensions.Tests.csproj b/tests/DNX.Extensions.Tests/DNX.Extensions.Tests.csproj index b861842..a05eff9 100644 --- a/tests/DNX.Extensions.Tests/DNX.Extensions.Tests.csproj +++ b/tests/DNX.Extensions.Tests/DNX.Extensions.Tests.csproj @@ -7,6 +7,7 @@ x64;x86;AnyCPU enable disable + false diff --git a/tests/DNX.Extensions.Tests/IO/DirectoryInfoExtensionsTests.cs b/tests/DNX.Extensions.Tests/IO/DirectoryInfoExtensionsTests.cs index f511b91..9055c00 100644 --- a/tests/DNX.Extensions.Tests/IO/DirectoryInfoExtensionsTests.cs +++ b/tests/DNX.Extensions.Tests/IO/DirectoryInfoExtensionsTests.cs @@ -246,6 +246,7 @@ public static TheoryData GetRelativePath_Data() { { Path.Combine(Path.GetTempPath(), guid1), null, null }, { null, Path.Combine(Path.GetTempPath(), guid1), null }, + { Path.Combine(Path.GetTempPath(), guid1), Path.Combine(Path.GetTempPath(), guid1), "" }, { Path.Combine(Path.GetTempPath(), guid1), Path.Combine(Path.GetTempPath(), "abcdefg"), Path.Join("..", guid1) }, { Path.Combine(Path.GetTempPath(), guid2), Path.Combine(Path.GetTempPath(), guid3), Path.Join("..", guid2) }, { Path.Combine(Path.GetTempPath(), "abcdefg"), Path.Combine(Path.GetTempPath(), "abcdefg"), "" },