diff --git a/.github/workflows/delayed-pipelines.yml b/.github/workflows/delayed-pipelines.yml
index 486193699..af88b86cb 100644
--- a/.github/workflows/delayed-pipelines.yml
+++ b/.github/workflows/delayed-pipelines.yml
@@ -19,48 +19,6 @@ on:
- Release
jobs:
- prepare_linux:
- name: đ§ Prepare Linux
- runs-on: ubuntu-22.04
- timeout-minutes: 15
- outputs:
- restoreCacheKey: ${{ steps.dotnet-restore.outputs.restoreCacheKey }}
- steps:
- - name: Checkout
- uses: codebeltnet/git-checkout@v1
-
- - name: Install .NET
- uses: codebeltnet/install-dotnet@v1
- with:
- includePreview: true
-
- - id: dotnet-restore
- name: Restore Dependencies
- uses: codebeltnet/dotnet-restore@v2
- with:
- useRestoreCache: true
-
- prepare_windows:
- name: đŞ Prepare Windows
- runs-on: windows-2022
- timeout-minutes: 15
- outputs:
- restoreCacheKey: ${{ steps.dotnet-restore.outputs.restoreCacheKey }}
- steps:
- - name: Checkout
- uses: codebeltnet/git-checkout@v1
-
- - name: Install .NET
- uses: codebeltnet/install-dotnet@v1
- with:
- includePreview: true
-
- - id: dotnet-restore
- name: Restore Dependencies
- uses: codebeltnet/dotnet-restore@v2
- with:
- useRestoreCache: true
-
build:
name: đ ď¸ Build
runs-on: ubuntu-22.04
@@ -69,7 +27,6 @@ jobs:
matrix:
configuration: [Debug, Release]
framework: [net9.0, net8.0, netstandard2.0]
- needs: [prepare_linux]
outputs:
version: ${{ steps.minver-calculate.outputs.version }}
projects: ${{ steps.projects.outputs.result }}
@@ -113,6 +70,9 @@ jobs:
fi
shell: bash
+ - name: Restore Dependencies
+ uses: codebeltnet/dotnet-restore@v2
+
- id: dotnet-build
name: Build for ${{ matrix.framework }} (${{ matrix.configuration }})
uses: codebeltnet/dotnet-build@v2
@@ -120,7 +80,6 @@ jobs:
projects: ${{ steps.projects.outputs.result }}
configuration: ${{ matrix.configuration }}
framework: ${{ matrix.framework }}
- restoreCacheKey: ${{ needs.prepare_linux.outputs.restoreCacheKey }}
uploadBuildArtifactName: 'DelayedBuild.${{ matrix.framework }}.${{ matrix.configuration }}'
pack:
@@ -130,7 +89,7 @@ jobs:
strategy:
matrix:
configuration: [Debug, Release]
- needs: [prepare_linux, build]
+ needs: [build]
steps:
- name: Checkout
uses: codebeltnet/git-checkout@v1
@@ -146,10 +105,11 @@ jobs:
configuration: ${{ matrix.configuration }}
uploadPackedArtifact: true
version: ${{ needs.build.outputs.version }}
- restoreCacheKey: ${{ needs.prepare_linux.outputs.restoreCacheKey }}
downloadBuildArtifactPattern: 'DelayedBuild.*.${{ matrix.configuration }}'
uploadPackedArtifactName: 'NuGet-Delayed-${{ matrix.configuration }}'
- projects: ${{ needs.build.outputs.projects }}
+ projects: >-
+ src/**/Cuemon.Core.App.csproj
+ src/**/Cuemon.Extensions.Globalization.csproj
deploy:
if: github.event_name != 'pull_request'
diff --git a/.github/workflows/pipelines.yml b/.github/workflows/pipelines.yml
index 6c74ad378..e2adaef88 100644
--- a/.github/workflows/pipelines.yml
+++ b/.github/workflows/pipelines.yml
@@ -221,7 +221,48 @@ jobs:
uploadPackedArtifact: true
version: ${{ needs.build.outputs.version }}
restoreCacheKey: ${{ needs.prepare_linux.outputs.restoreCacheKey }}
- projects: ${{ needs.build.outputs.projects }}
+ projects: >-
+ src/**/Cuemon.AspNetCore.csproj
+ src/**/Cuemon.AspNetCore.App.csproj
+ src/**/Cuemon.AspNetCore.Authentication.csproj
+ src/**/Cuemon.AspNetCore.Mvc.csproj
+ src/**/Cuemon.AspNetCore.Razor.TagHelpers.csproj
+ src/**/Cuemon.Core.csproj
+ src/**/Cuemon.Data.csproj
+ src/**/Cuemon.Data.Integrity.csproj
+ src/**/Cuemon.Data.SqlClient.csproj
+ src/**/Cuemon.Diagnostics.csproj
+ src/**/Cuemon.Extensions.AspNetCore.csproj
+ src/**/Cuemon.Extensions.AspNetCore.Authentication.csproj
+ src/**/Cuemon.Extensions.AspNetCore.Mvc.csproj
+ src/**/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Text.Json.csproj
+ src/**/Cuemon.Extensions.AspNetCore.Mvc.Formatters.Xml.csproj
+ src/**/Cuemon.Extensions.AspNetCore.Mvc.RazorPages.csproj
+ src/**/Cuemon.Extensions.AspNetCore.Text.Json.csproj
+ src/**/Cuemon.Extensions.AspNetCore.Xml.csproj
+ src/**/Cuemon.Extensions.Collections.Generic.csproj
+ src/**/Cuemon.Extensions.Collections.Specialized.csproj
+ src/**/Cuemon.Extensions.Core.csproj
+ src/**/Cuemon.Extensions.Data.csproj
+ src/**/Cuemon.Extensions.Data.Integrity.csproj
+ src/**/Cuemon.Extensions.DependencyInjection.csproj
+ src/**/Cuemon.Extensions.Diagnostics.csproj
+ src/**/Cuemon.Extensions.Hosting.csproj
+ src/**/Cuemon.Extensions.IO.csproj
+ src/**/Cuemon.Extensions.Net.csproj
+ src/**/Cuemon.Extensions.Reflection.csproj
+ src/**/Cuemon.Extensions.Runtime.Caching.csproj
+ src/**/Cuemon.Extensions.Text.csproj
+ src/**/Cuemon.Extensions.Text.Json.csproj
+ src/**/Cuemon.Extensions.Threading.csproj
+ src/**/Cuemon.Extensions.Xml.csproj
+ src/**/Cuemon.IO.csproj
+ src/**/Cuemon.Net.csproj
+ src/**/Cuemon.Resilience.csproj
+ src/**/Cuemon.Runtime.Caching.csproj
+ src/**/Cuemon.Security.Cryptography.csproj
+ src/**/Cuemon.Threading.csproj
+ src/**/Cuemon.Xml.csproj
test:
name: đ§Ş Test
@@ -262,7 +303,7 @@ jobs:
projects: ${{ matrix.project }}
configuration: ${{ matrix.configuration }}
restoreCacheKey: ${{ runner.os == 'Linux' && needs.prepare_linux.outputs.restoreCacheKey || needs.prepare_windows.outputs.restoreCacheKey }}
- buildSwitches: -p:SkipSignAssembly=true
+ buildSwitches: ${{ contains(matrix.project, 'Cuemon.Extensions.Globalization.Tests') && '-p:SkipSignAssembly=false' || '-p:SkipSignAssembly=true' }}
testArguments: -- RunConfiguration.DisableAppDomain=true
env:
CONNECTIONSTRINGS__ADVENTUREWORKS: ${{ secrets.DB_ADVENTUREWORKS }}
@@ -296,6 +337,13 @@ jobs:
useRestoreCache: true
restoreCacheKey: ${{ needs.prepare_linux.outputs.restoreCacheKey }}
+ - name: Download cuemon.snk file
+ uses: codebeltnet/gcp-download-file@v1
+ with:
+ serviceAccountKey: ${{ secrets.GCP_TOKEN }}
+ bucketName: ${{ secrets.GCP_BUCKETNAME }}
+ objectName: cuemon.snk
+
- name: Run SonarCloud Analysis
uses: codebeltnet/sonarcloud-scan@v1
with:
@@ -308,7 +356,6 @@ jobs:
uses: codebeltnet/dotnet-build@v2
with:
uploadBuildArtifact: false
- buildSwitches: -p:SkipSignAssembly=true
- name: Finalize SonarCloud Analysis
uses: codebeltnet/sonarcloud-scan-finalize@v1
@@ -353,11 +400,17 @@ jobs:
- name: Prepare CodeQL SAST Analysis
uses: codebeltnet/codeql-scan@v1
+ - name: Download cuemon.snk file
+ uses: codebeltnet/gcp-download-file@v1
+ with:
+ serviceAccountKey: ${{ secrets.GCP_TOKEN }}
+ bucketName: ${{ secrets.GCP_BUCKETNAME }}
+ objectName: cuemon.snk
+
- name: Build
uses: codebeltnet/dotnet-build@v2
with:
uploadBuildArtifact: false
- buildSwitches: -p:SkipSignAssembly=true
- name: Finalize CodeQL SAST Analysis
uses: codebeltnet/codeql-scan-finalize@v1
diff --git a/Directory.Build.props b/Directory.Build.props
index 9f4e016c2..2025b1d8b 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -5,7 +5,7 @@
$([MSBuild]::IsOSPlatform('Windows'))
true
false
- preview
+ latest
diff --git a/Directory.Packages.props b/Directory.Packages.props
index e391f3224..6318a7a12 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -8,7 +8,7 @@
-
+
diff --git a/src/Cuemon.Extensions.Globalization/Cuemon.Extensions.Globalization.csproj b/src/Cuemon.Extensions.Globalization/Cuemon.Extensions.Globalization.csproj
index 40dd52c32..60d6a4383 100644
--- a/src/Cuemon.Extensions.Globalization/Cuemon.Extensions.Globalization.csproj
+++ b/src/Cuemon.Extensions.Globalization/Cuemon.Extensions.Globalization.csproj
@@ -1203,4 +1203,9 @@
+
+
+
+
+
diff --git a/test/Cuemon.Extensions.Globalization.Tests/Cuemon.Extensions.Globalization.Tests.csproj b/test/Cuemon.Extensions.Globalization.Tests/Cuemon.Extensions.Globalization.Tests.csproj
index 217ed8211..3ae2139fa 100644
--- a/test/Cuemon.Extensions.Globalization.Tests/Cuemon.Extensions.Globalization.Tests.csproj
+++ b/test/Cuemon.Extensions.Globalization.Tests/Cuemon.Extensions.Globalization.Tests.csproj
@@ -5,6 +5,7 @@
+
diff --git a/test/Cuemon.Extensions.Globalization.Tests/YamlSerializerTest.cs b/test/Cuemon.Extensions.Globalization.Tests/YamlSerializerTest.cs
new file mode 100644
index 000000000..934ec0356
--- /dev/null
+++ b/test/Cuemon.Extensions.Globalization.Tests/YamlSerializerTest.cs
@@ -0,0 +1,450 @@
+ďťżusing System;
+using System.Globalization;
+using System.Linq;
+using System.Runtime.InteropServices;
+using Codebelt.Extensions.Xunit;
+using Codebelt.Extensions.YamlDotNet;
+using Codebelt.Extensions.YamlDotNet.Formatters;
+using Cuemon.Extensions.IO;
+using Cuemon.Extensions.Reflection;
+using Xunit;
+using Xunit.Abstractions;
+using YamlDotNet.Core;
+using YamlDotNet.Serialization.NamingConventions;
+
+namespace Cuemon.Extensions.Globalization
+{
+ public class YamlSerializerTest : Test
+ {
+ private readonly CultureInfo _cultureInfo;
+
+ public YamlSerializerTest(ITestOutputHelper output) : base(output)
+ {
+ output.WriteLine("Assembly version: " + typeof(CultureInfoExtensions).Assembly.GetAssemblyVersion().ToString());
+ try
+ {
+ _cultureInfo = new CultureInfo("da-DK").UseNationalLanguageSupport(); // from .NET6+ this is needed for both Windows and Linux; at least from pipeline (worked locally for Windows without Merge ...)
+ }
+ catch (Exception e)
+ {
+ output.WriteLine(e.ToString());
+ }
+ }
+
+ [Fact]
+ public void Serialize_ShouldSerializeDateFormatInfo()
+ {
+ var sut2 = _cultureInfo;
+ var sut3 = YamlFormatter.SerializeObject(sut2.DateTimeFormat, o =>
+ {
+ o.Settings.NamingConvention = PascalCaseNamingConvention.Instance;
+ o.Settings.ScalarStyle = ScalarStyle.Plain;
+ o.Settings.IndentSequences = false;
+ o.Settings.FormatProvider = _cultureInfo;
+ o.Settings.Converters.Add(YamlConverterFactory.Create((writer, dt, _) => writer.WriteValue(dt.ToString(_cultureInfo))));
+ });
+ var sut4 = sut3.ToEncodedString();
+
+ TestOutput.WriteLine(sut4);
+
+ var expected = @"AMDesignator: ''
+Calendar:
+ MinSupportedDateTime: 01-01-0001 00:00:00
+ MaxSupportedDateTime: 31-12-9999 23:59:59
+ AlgorithmType: SolarCalendar
+ CalendarType: Localized
+ Eras:
+ - 1
+ TwoDigitYearMax: {0}
+DateSeparator: '-'
+FirstDayOfWeek: Monday
+CalendarWeekRule: FirstFourDayWeek
+FullDateTimePattern: d. MMMM yyyy HH:mm:ss
+LongDatePattern: d. MMMM yyyy
+LongTimePattern: HH:mm:ss
+MonthDayPattern: d. MMMM
+PMDesignator: ''
+RFC1123Pattern: ddd, dd MMM yyyy HH':'mm':'ss 'GMT'
+ShortDatePattern: dd-MM-yyyy
+ShortTimePattern: HH:mm
+SortableDateTimePattern: yyyy'-'MM'-'dd'T'HH':'mm':'ss
+TimeSeparator: ':'
+UniversalSortableDateTimePattern: yyyy'-'MM'-'dd HH':'mm':'ss'Z'
+YearMonthPattern: MMMM yyyy
+AbbreviatedDayNames:
+- sø
+- ma
+- ti
+- on
+- to
+- fr
+- lø
+ShortestDayNames:
+- sø
+- ma
+- ti
+- on
+- to
+- fr
+- lø
+DayNames:
+- søndag
+- mandag
+- tirsdag
+- onsdag
+- torsdag
+- fredag
+- lørdag
+AbbreviatedMonthNames:
+- jan
+- feb
+- mar
+- apr
+- maj
+- jun
+- jul
+- aug
+- sep
+- okt
+- nov
+- dec
+- ''
+MonthNames:
+- januar
+- februar
+- marts
+- april
+- maj
+- juni
+- juli
+- august
+- september
+- oktober
+- november
+- december
+- ''
+NativeCalendarName: gregoriansk kalender
+AbbreviatedMonthGenitiveNames:
+- jan
+- feb
+- mar
+- apr
+- maj
+- jun
+- jul
+- aug
+- sep
+- okt
+- nov
+- dec
+- ''
+MonthGenitiveNames:
+- januar
+- februar
+- marts
+- april
+- maj
+- juni
+- juli
+- august
+- september
+- oktober
+- november
+- december
+- ''
+".ReplaceLineEndings();
+
+#if NET8_0_OR_GREATER
+ expected = string.Format(expected, "2049");
+#elif NET48_OR_GREATER
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
+ {
+ expected = expected.ReplaceAll("gregoriansk kalender", "dansk (Danmark)", StringComparison.Ordinal);
+ expected = string.Format(expected, "2029");
+ }
+ else
+ {
+ expected = expected.ReplaceAll("gregoriansk", "Gregoriansk", StringComparison.Ordinal);
+ expected = string.Format(expected, "2049");
+ }
+#endif
+
+ Assert.Equal(expected, sut4);
+ }
+
+ [Fact]
+ public void Serialize_ShouldSerializeNumberFormatInfo()
+ {
+ var sut2 = _cultureInfo;
+ var sut3 = YamlFormatter.SerializeObject(sut2.NumberFormat, o =>
+ {
+ o.Settings.ScalarStyle = ScalarStyle.DoubleQuoted;
+ o.Settings.IndentSequences = false;
+ o.Settings.FormatProvider = _cultureInfo;
+ o.Settings.NamingConvention = PascalCaseNamingConvention.Instance;
+ });
+ var sut4 = sut3.ToEncodedString();
+
+ TestOutput.WriteLine(sut4);
+
+ Assert.Equal(@"CurrencyDecimalDigits: 2
+CurrencyDecimalSeparator: "",""
+CurrencyGroupSizes:
+- 3
+NumberGroupSizes:
+- 3
+PercentGroupSizes:
+- 3
+CurrencyGroupSeparator: "".""
+CurrencySymbol: ""kr.""
+NaNSymbol: ""NaN""
+CurrencyNegativePattern: 8
+NumberNegativePattern: 1
+PercentPositivePattern: 0
+PercentNegativePattern: 0
+NegativeInfinitySymbol: ""-â""
+NegativeSign: ""-""
+NumberDecimalDigits: 2
+NumberDecimalSeparator: "",""
+NumberGroupSeparator: "".""
+CurrencyPositivePattern: 3
+PositiveInfinitySymbol: ""â""
+PositiveSign: ""+""
+PercentDecimalDigits: 2
+PercentDecimalSeparator: "",""
+PercentGroupSeparator: "".""
+PercentSymbol: ""%""
+PerMilleSymbol: ""â°""
+NativeDigits:
+- ""0""
+- ""1""
+- ""2""
+- ""3""
+- ""4""
+- ""5""
+- ""6""
+- ""7""
+- ""8""
+- ""9""
+DigitSubstitution: ""None""
+".ReplaceLineEndings(), sut4);
+ }
+
+
+ [Fact]
+ public void Serialize_ShouldSerializeCultureInfo()
+ {
+ var sut2 = _cultureInfo;
+ var sut3 = YamlFormatter.SerializeObject(sut2, o =>
+ {
+ o.Settings.IndentSequences = false;
+ o.Settings.NamingConvention = PascalCaseNamingConvention.Instance;
+ o.Settings.Converters.Add(YamlConverterFactory.Create((writer, dt, _) => writer.WriteValue(dt.ToString(_cultureInfo))));
+ });
+ var sut4 = sut3.ToEncodedString().ReplaceLineEndings().Split(new[] { Environment.NewLine }, StringSplitOptions.None).ToList();
+
+ sut4.RemoveRange(sut4.FindIndex(s => s.StartsWith("CompareInfo")), 6);
+ sut4.RemoveRange(sut4.FindIndex(s => s.StartsWith("CultureTypes")), 1);
+
+ var expected = @"LCID: 1030
+KeyboardLayoutId: 1030
+Name: da-DK
+IetfLanguageTag: da-DK
+DisplayName: Danish (Denmark)
+NativeName: dansk (Danmark)
+EnglishName: Danish (Denmark)
+TwoLetterISOLanguageName: da
+ThreeLetterISOLanguageName: dan
+ThreeLetterWindowsLanguageName: DAN
+TextInfo:
+ ANSICodePage: 1252
+ OEMCodePage: 850
+ MacCodePage: 10000
+ EBCDICCodePage: 20277
+ LCID: 1030
+ CultureName: da-DK
+ ListSeparator: ;
+ IsRightToLeft: false
+IsNeutralCulture: false
+NumberFormat:
+ CurrencyDecimalDigits: 2
+ CurrencyDecimalSeparator: ','
+ CurrencyGroupSizes:
+ - 3
+ NumberGroupSizes:
+ - 3
+ PercentGroupSizes:
+ - 3
+ CurrencyGroupSeparator: .
+ CurrencySymbol: kr.
+ NaNSymbol: NaN
+ CurrencyNegativePattern: 8
+ NumberNegativePattern: 1
+ PercentPositivePattern: 0
+ PercentNegativePattern: 0
+ NegativeInfinitySymbol: -â
+ NegativeSign: '-'
+ NumberDecimalDigits: 2
+ NumberDecimalSeparator: ','
+ NumberGroupSeparator: .
+ CurrencyPositivePattern: 3
+ PositiveInfinitySymbol: â
+ PositiveSign: +
+ PercentDecimalDigits: 2
+ PercentDecimalSeparator: ','
+ PercentGroupSeparator: .
+ PercentSymbol: '%'
+ PerMilleSymbol: â°
+ NativeDigits:
+ - 0
+ - 1
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ - 9
+ DigitSubstitution: None
+DateTimeFormat:
+ AMDesignator: ''
+ Calendar:
+ MinSupportedDateTime: 01-01-0001 00:00:00
+ MaxSupportedDateTime: 31-12-9999 23:59:59
+ AlgorithmType: SolarCalendar
+ CalendarType: Localized
+ Eras:
+ - 1
+ TwoDigitYearMax: {0}
+ DateSeparator: '-'
+ FirstDayOfWeek: Monday
+ CalendarWeekRule: FirstFourDayWeek
+ FullDateTimePattern: d. MMMM yyyy HH:mm:ss
+ LongDatePattern: d. MMMM yyyy
+ LongTimePattern: HH:mm:ss
+ MonthDayPattern: d. MMMM
+ PMDesignator: ''
+ RFC1123Pattern: ddd, dd MMM yyyy HH':'mm':'ss 'GMT'
+ ShortDatePattern: dd-MM-yyyy
+ ShortTimePattern: HH:mm
+ SortableDateTimePattern: yyyy'-'MM'-'dd'T'HH':'mm':'ss
+ TimeSeparator: ':'
+ UniversalSortableDateTimePattern: yyyy'-'MM'-'dd HH':'mm':'ss'Z'
+ YearMonthPattern: MMMM yyyy
+ AbbreviatedDayNames:
+ - sø
+ - ma
+ - ti
+ - on
+ - to
+ - fr
+ - lø
+ ShortestDayNames:
+ - sø
+ - ma
+ - ti
+ - on
+ - to
+ - fr
+ - lø
+ DayNames:
+ - søndag
+ - mandag
+ - tirsdag
+ - onsdag
+ - torsdag
+ - fredag
+ - lørdag
+ AbbreviatedMonthNames:
+ - jan
+ - feb
+ - mar
+ - apr
+ - maj
+ - jun
+ - jul
+ - aug
+ - sep
+ - okt
+ - nov
+ - dec
+ - ''
+ MonthNames:
+ - januar
+ - februar
+ - marts
+ - april
+ - maj
+ - juni
+ - juli
+ - august
+ - september
+ - oktober
+ - november
+ - december
+ - ''
+ NativeCalendarName: gregoriansk kalender
+ AbbreviatedMonthGenitiveNames:
+ - jan
+ - feb
+ - mar
+ - apr
+ - maj
+ - jun
+ - jul
+ - aug
+ - sep
+ - okt
+ - nov
+ - dec
+ - ''
+ MonthGenitiveNames:
+ - januar
+ - februar
+ - marts
+ - april
+ - maj
+ - juni
+ - juli
+ - august
+ - september
+ - oktober
+ - november
+ - december
+ - ''
+Calendar:
+ MinSupportedDateTime: 01-01-0001 00:00:00
+ MaxSupportedDateTime: 31-12-9999 23:59:59
+ AlgorithmType: SolarCalendar
+ CalendarType: Localized
+ Eras:
+ - 1
+ TwoDigitYearMax: {0}
+OptionalCalendars:
+- MinSupportedDateTime: 01-01-0001 00:00:00
+ MaxSupportedDateTime: 31-12-9999 23:59:59
+ AlgorithmType: SolarCalendar
+ CalendarType: Localized
+ Eras:
+ - 1
+ TwoDigitYearMax: {0}
+UseUserOverride: true
+";
+
+#if NET8_0_OR_GREATER || NET48_OR_GREATER
+ expected = string.Format(expected, "2049");
+#else
+ expected = string.Format(expected, "2029");
+#endif
+
+#if NET48_OR_GREATER
+ expected = expected.ReplaceAll("gregoriansk", "Gregoriansk", StringComparison.Ordinal);
+#endif
+
+ TestOutput.WriteLines(sut4);
+
+ Assert.Equal(expected.ReplaceLineEndings().Split(new[] { Environment.NewLine }, StringSplitOptions.None).ToList(), sut4);
+ }
+ }
+}