diff --git a/.github/actions/install-zstd/action.yml b/.github/actions/install-zstd/action.yml index d60418b1df..fa103b0ec5 100644 --- a/.github/actions/install-zstd/action.yml +++ b/.github/actions/install-zstd/action.yml @@ -8,6 +8,10 @@ inputs: description: 'zstd version' required: false default: '1.5.7' + checksum: + description: 'zstd checksum' + required: false + default: 'acb4e8111511749dc7a3ebedca9b04190e37a17afeb73f55d4425dbf0b90fad9' runs: using: composite @@ -17,10 +21,16 @@ runs: shell: pwsh env: ZSTD_VERSION: ${{ inputs.version }} + ZSTD_CHECKSUM: ${{ inputs.checksum }} run: | $url = "https://github.com/facebook/zstd/releases/download/v$env:ZSTD_VERSION/zstd-v$env:ZSTD_VERSION-win64.zip" $installDir = "$env:RUNNER_TOOL_CACHE\zstd-v$env:ZSTD_VERSION-win64" Invoke-WebRequest -OutFile "$env:TEMP\zstd.zip" -Uri $url + $checksum = (Get-FileHash "$env:TEMP\zstd.zip" -Algorithm SHA256).Hash.ToLower() + if ($checksum -ne $env:ZSTD_CHECKSUM) { + Write-Error "zstd checksum verification failed. Expected: $env:ZSTD_CHECKSUM, Actual: $checksum" + exit 1 + } Expand-Archive -Path "$env:TEMP\zstd.zip" -DestinationPath $env:RUNNER_TOOL_CACHE -Force echo "$installDir" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append & "$installDir\zstd.exe" --version diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dc93dc464e..d438557fce 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -257,7 +257,7 @@ jobs: - name: Fetch NuGet Packages if: env.CI_PUBLISHING_BUILD == 'true' - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: ${{ github.sha }} path: src @@ -338,7 +338,7 @@ jobs: uses: ./.github/actions/buildnative - name: Fetch NuGet Packages - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: ${{ github.sha }} path: src diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 88ffeee0b9..5a73102373 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: uses: ./.github/actions/environment - name: Initialize CodeQL - uses: github/codeql-action/init@51f77329afa6477de8c49fc9c7046c15b9a4e79d # pin@v2 + uses: github/codeql-action/init@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # pin@v2 with: languages: csharp @@ -49,6 +49,6 @@ jobs: run: dotnet build Sentry-CI-CodeQL.slnf --no-restore --nologo - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@51f77329afa6477de8c49fc9c7046c15b9a4e79d # pin@v2 + uses: github/codeql-action/analyze@76621b61decf072c1cee8dd1ce2d2a82d33c17ed # pin@v2 with: category: '/language:csharp' diff --git a/.github/workflows/device-tests-android.yml b/.github/workflows/device-tests-android.yml index e8c95eed8b..d58729dea9 100644 --- a/.github/workflows/device-tests-android.yml +++ b/.github/workflows/device-tests-android.yml @@ -76,13 +76,13 @@ jobs: uses: actions/checkout@v4 - name: Download test app artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v5 with: name: device-test-android-${{ matrix.tfm }} path: bin - name: Setup Gradle - uses: gradle/actions/setup-gradle@ac638b010cf58a27ee6c972d7336334ccaf61c96 # pin@v3 + uses: gradle/actions/setup-gradle@017a9effdb900e5b5b2fddfb590a105619dca3c3 # pin@v3 # Cached AVD setup per https://github.com/ReactiveCircus/android-emulator-runner/blob/main/README.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 7849f02df9..8afbbc57ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 5.14.1 + +### Fixes + +- Crontabs now support day names (MON-SUN) and allow step values and ranges to be combined ([#4407](https://github.com/getsentry/sentry-dotnet/pull/4407)) +- Ensure the correct Sentry Cocoa SDK framework version is used on iOS ([#4411](https://github.com/getsentry/sentry-dotnet/pull/4411)) + +### Dependencies + +- Bump CLI from v2.50.2 to v2.51.1 ([#4419](https://github.com/getsentry/sentry-dotnet/pull/4419), [#4435](https://github.com/getsentry/sentry-dotnet/pull/4435)) + - [changelog](https://github.com/getsentry/sentry-cli/blob/master/CHANGELOG.md#2511) + - [diff](https://github.com/getsentry/sentry-cli/compare/2.50.2...2.51.1) + ## 5.14.0 ### Features diff --git a/Directory.Build.props b/Directory.Build.props index 4e0c3a33cf..73293414ee 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,7 +1,7 @@ - 5.14.0 + 5.14.1 13 true true @@ -86,7 +86,7 @@ - 2.50.2 + 2.51.1 $(MSBuildThisFileDirectory)tools\sentry-cli\$(SentryCLIVersion)\ diff --git a/Sentry-CI-Build-Windows-arm64.slnf b/Sentry-CI-Build-Windows-arm64.slnf index c1f6bd735d..baac3fedfb 100644 --- a/Sentry-CI-Build-Windows-arm64.slnf +++ b/Sentry-CI-Build-Windows-arm64.slnf @@ -55,7 +55,6 @@ "src\\Sentry.SourceGenerators\\Sentry.SourceGenerators.csproj", "src\\Sentry\\Sentry.csproj", "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", - "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", "test\\Sentry.AspNet.Tests\\Sentry.AspNet.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", "test\\Sentry.AspNetCore.Tests\\Sentry.AspNetCore.Tests.csproj", diff --git a/Sentry-CI-Build-Windows.slnf b/Sentry-CI-Build-Windows.slnf index d5960f2212..677a8f0608 100644 --- a/Sentry-CI-Build-Windows.slnf +++ b/Sentry-CI-Build-Windows.slnf @@ -55,7 +55,6 @@ "src\\Sentry.SourceGenerators\\Sentry.SourceGenerators.csproj", "src\\Sentry\\Sentry.csproj", "test\\Sentry.Analyzers.Tests\\Sentry.Analyzers.Tests.csproj", - "test\\Sentry.Android.AssemblyReader.Tests\\Sentry.Android.AssemblyReader.Tests.csproj", "test\\Sentry.AspNet.Tests\\Sentry.AspNet.Tests.csproj", "test\\Sentry.AspNetCore.Grpc.Tests\\Sentry.AspNetCore.Grpc.Tests.csproj", "test\\Sentry.AspNetCore.Tests\\Sentry.AspNetCore.Tests.csproj", diff --git a/scripts/generate-solution-filters-config.yaml b/scripts/generate-solution-filters-config.yaml index da07eaaa75..45c95d8f4b 100644 --- a/scripts/generate-solution-filters-config.yaml +++ b/scripts/generate-solution-filters-config.yaml @@ -96,6 +96,8 @@ filterConfigs: - "trimTests" patterns: - "**/*AndroidTestApp.csproj" + # AssemblyReader tests are flaky on Windows: https://github.com/getsentry/sentry-dotnet/issues/4091 + - "**/*Sentry.Android.AssemblyReader.Tests.csproj" - "**/*DeviceTests*.csproj" - "**/*Sentry.Maui.Device.TestApp.csproj" - "**/*Sentry.Samples.Android.csproj" @@ -115,6 +117,8 @@ filterConfigs: - "trimTests" patterns: - "**/*AndroidTestApp.csproj" + # AssemblyReader tests are flaky on Windows: https://github.com/getsentry/sentry-dotnet/issues/4091 + - "**/*Sentry.Android.AssemblyReader.Tests.csproj" - "**/*DeviceTests*.csproj" - "**/*Sentry.Maui.Device.TestApp.csproj" - "**/*Sentry.Samples.Android.csproj" diff --git a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj index 60364aae34..5d175c990b 100644 --- a/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj +++ b/src/Sentry.Bindings.Cocoa/Sentry.Bindings.Cocoa.csproj @@ -9,7 +9,9 @@ .NET Bindings for the Sentry Cocoa SDK ..\..\modules\sentry-cocoa\ $(SentryCocoaCache)Sentry.framework\ - $(SentryCocoaCache)Sentry-Dynamic.xcframework + $([System.IO.File]::ReadAllText("$(MSBuildThisFileDirectory)../../modules/sentry-cocoa.properties")) + $([System.Text.RegularExpressions.Regex]::Match($(SentryCocoaProperties), 'version\s*=\s*([^\s]+)').Groups[1].Value) + $(SentryCocoaCache)Sentry-$(SentryCocoaVersion).xcframework @@ -46,20 +48,14 @@ - - - - $([System.IO.File]::ReadAllText("../../modules/sentry-cocoa.properties")) - $([System.Text.RegularExpressions.Regex]::Match($(PropertiesContent), 'version\s*=\s*([^\s]+)').Groups[1].Value) - + - + @@ -67,23 +63,35 @@ + Condition="!Exists('$(SentryCocoaFramework).zip')" + Command="curl -L https://github.com/getsentry/sentry-cocoa/releases/download/$(SentryCocoaVersion)/Sentry-Dynamic.xcframework.zip -o $(SentryCocoaFramework).zip" /> + Condition="Exists('$(SentryCocoaFramework).zip') and !Exists('$(SentryCocoaFramework)')" + Command="unzip -o $(SentryCocoaFramework).zip -d $(SentryCocoaCache) && mv $(SentryCocoaCache)Sentry-Dynamic.xcframework $(SentryCocoaFramework)" /> - + + + + + + + + + diff --git a/src/Sentry/Sentry.csproj b/src/Sentry/Sentry.csproj index 1ba7e0cc97..e1aac028ff 100644 --- a/src/Sentry/Sentry.csproj +++ b/src/Sentry/Sentry.csproj @@ -113,13 +113,13 @@ <_OSArchitecture>$([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture) - - - - - - - + + + + + + + diff --git a/src/Sentry/SentryMonitorOptions.cs b/src/Sentry/SentryMonitorOptions.cs index 9ec7406822..8d566beedc 100644 --- a/src/Sentry/SentryMonitorOptions.cs +++ b/src/Sentry/SentryMonitorOptions.cs @@ -57,15 +57,17 @@ public partial class SentryMonitorOptions : ISentryJsonSerializable // - Allows */n for step values where n must be any positive integer (except zero) // - Allows single values within their valid ranges // - Allows ranges (e.g., 8-10) + // - Allows step values with ranges (e.g., 8-18/4) // - Allows lists of values and ranges (e.g., 6,8,9 or 8-10,12-14) + // - Allows weekday names (MON, TUE, WED, THU, FRI, SAT, SUN) // // Valid ranges for each field: // - Minutes: 0-59 // - Hours: 0-23 // - Days: 1-31 // - Months: 1-12 - // - Weekdays: 0-7 (0 and 7 both represent Sunday) - private const string ValidCrontabPattern = @"^(\*|(\*\/([1-9][0-9]*))|([0-5]?\d)(-[0-5]?\d)?)(,([0-5]?\d)(-[0-5]?\d)?)*(\s+)(\*|(\*\/([1-9][0-9]*))|([01]?\d|2[0-3])(-([01]?\d|2[0-3]))?)((,([01]?\d|2[0-3])(-([01]?\d|2[0-3]))?)*)?(\s+)(\*|(\*\/([1-9][0-9]*))|([1-9]|[12]\d|3[01])(-([1-9]|[12]\d|3[01]))?)((,([1-9]|[12]\d|3[01])(-([1-9]|[12]\d|3[01]))?)*)?(\s+)(\*|(\*\/([1-9][0-9]*))|([1-9]|1[0-2])(-([1-9]|1[0-2]))?)((,([1-9]|1[0-2])(-([1-9]|1[0-2]))?)*)?(\s+)(\*|(\*\/([1-9][0-9]*))|[0-7](-[0-7])?)((,[0-7](-[0-7])?)*)?$"; + // - Weekdays: 0-7 (0 and 7 both represent Sunday) or MON-SUN + private const string ValidCrontabPattern = @"^(\*(\/([1-9][0-9]*))?|([0-5]?\d)|([0-5]?\d)-([0-5]?\d)(\/([1-9][0-9]*))?)(,(\*(\/([1-9][0-9]*))?|([0-5]?\d)|([0-5]?\d)-([0-5]?\d)(\/([1-9][0-9]*))?))*(\s+)(\*(\/([1-9][0-9]*))?|([01]?\d|2[0-3])|([01]?\d|2[0-3])-([01]?\d|2[0-3])(\/([1-9][0-9]*))?)(,(\*(\/([1-9][0-9]*))?|([01]?\d|2[0-3])|([01]?\d|2[0-3])-([01]?\d|2[0-3])(\/([1-9][0-9]*))?))*(\s+)(\*(\/([1-9][0-9]*))?|([1-9]|[12]\d|3[01])|([1-9]|[12]\d|3[01])-([1-9]|[12]\d|3[01])(\/([1-9][0-9]*))?)(,(\*(\/([1-9][0-9]*))?|([1-9]|[12]\d|3[01])|([1-9]|[12]\d|3[01])-([1-9]|[12]\d|3[01])(\/([1-9][0-9]*))?))*(\s+)(\*(\/([1-9][0-9]*))?|([1-9]|1[0-2])|([1-9]|1[0-2])-([1-9]|1[0-2])(\/([1-9][0-9]*))?)(,(\*(\/([1-9][0-9]*))?|([1-9]|1[0-2])|([1-9]|1[0-2])-([1-9]|1[0-2])(\/([1-9][0-9]*))?))*(\s+)(\*(\/([1-9][0-9]*))?|[0-7]|(MON|TUE|WED|THU|FRI|SAT|SUN)|[0-7]-[0-7](\/([1-9][0-9]*))?|(MON|TUE|WED|THU|FRI|SAT|SUN)-(MON|TUE|WED|THU|FRI|SAT|SUN)(\/([1-9][0-9]*))?)(,(\*(\/([1-9][0-9]*))?|[0-7]|(MON|TUE|WED|THU|FRI|SAT|SUN)|[0-7]-[0-7](\/([1-9][0-9]*))?|(MON|TUE|WED|THU|FRI|SAT|SUN)-(MON|TUE|WED|THU|FRI|SAT|SUN)(\/([1-9][0-9]*))?))*$"; private SentryMonitorScheduleType _type = SentryMonitorScheduleType.None; private string? _crontab; @@ -73,14 +75,14 @@ public partial class SentryMonitorOptions : ISentryJsonSerializable private SentryMonitorInterval? _unit; #if NET9_0_OR_GREATER - [GeneratedRegex(ValidCrontabPattern, RegexOptions.IgnoreCase)] + [GeneratedRegex(ValidCrontabPattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture)] private static partial Regex ValidCrontab { get; } #elif NET8_0 - [GeneratedRegex(ValidCrontabPattern, RegexOptions.IgnoreCase)] + [GeneratedRegex(ValidCrontabPattern, RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture)] private static partial Regex ValidCrontabRegex(); private static readonly Regex ValidCrontab = ValidCrontabRegex(); #else - private static readonly Regex ValidCrontab = new(ValidCrontabPattern, RegexOptions.Compiled | RegexOptions.CultureInvariant); + private static readonly Regex ValidCrontab = new(ValidCrontabPattern, RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); #endif /// diff --git a/test/Sentry.Tests/SentryMonitorOptionsTests.cs b/test/Sentry.Tests/SentryMonitorOptionsTests.cs index 86891f8a6e..184be0f9fe 100644 --- a/test/Sentry.Tests/SentryMonitorOptionsTests.cs +++ b/test/Sentry.Tests/SentryMonitorOptionsTests.cs @@ -19,13 +19,13 @@ day of month 1-31 / step values */ [Theory] - [InlineData("* * * * *")] - [InlineData("0 0 1 1 *")] - [InlineData("0 0 1 * 0")] - [InlineData("59 23 31 12 7")] - [InlineData("0 */2 * * *")] - [InlineData("0 8-10 * * *")] - [InlineData("0 6,8,9 * * *")] + [InlineData("* * * * *")] // Every minute + [InlineData("0 0 1 1 *")] // At midnight on January 1st + [InlineData("0 0 1 * 0")] // At midnight on the first day of the month if it's Sunday + [InlineData("59 23 31 12 7")] // At 23:59 on December 31st if it's Sunday + [InlineData("0 */2 * * *")] // Every 2 hours + [InlineData("0 8-10 * * *")] // At 8, 9, and 10 AM every day + [InlineData("0 6,8,9 * * *")] // At 6, 8, and 9 AM every day // Step values (*/n) [InlineData("*/15 * * * *")] // Every 15 minutes [InlineData("0 */6 * * *")] // Every 6 hours @@ -53,6 +53,34 @@ day of month 1-31 [InlineData("0 0 1 1 0")] // Minimum values [InlineData("*/1 * * * *")] // Step of 1 [InlineData("* * 31 */2 *")] // 31st of every other month + // Weekday names + [InlineData("0 0 * * MON")] // Monday only (weekday name) + [InlineData("0 9 * * MON-FRI")] // 9 AM, Monday to Friday (weekday range) + [InlineData("0 18 * * MON-FRI")] // 6 PM, Monday to Friday (weekday range) + [InlineData("0 0 * * MON-FRI")] // Midnight, Monday to Friday (weekday range) + [InlineData("0 20 * * MON-FRI")] // 8 PM, Monday to Friday (weekday range) + // Step values with ranges + [InlineData("0-30/15 * * * *")] // Every 15 minutes from 0-30 + [InlineData("0-59/10 * * * *")] // Every 10 minutes from 0-59 + [InlineData("0-45/5 * * * *")] // Every 5 minutes from 0-45 + [InlineData("* 8-18/2 * * *")] // Every 2 hours from 8-18 + [InlineData("* 0-23/6 * * *")] // Every 6 hours from 0-23 + [InlineData("* 9-17/1 * * *")] // Every hour from 9-17 + [InlineData("* * 1-15/3 * *")] // Every 3 days from 1-15 + [InlineData("* * 1-31/7 * *")] // Every 7 days from 1-31 + [InlineData("* * 10-20/2 * *")] // Every 2 days from 10-20 + [InlineData("* * * 1-6/2 *")] // Every 2 months from January-June + [InlineData("* * * 1-12/3 *")] // Every 3 months from January-December + [InlineData("* * * 3-9/1 *")] // Every month from March-September + [InlineData("* * * * 1-5/2")] // Every 2 weekdays from 1-5 (Mon-Fri) + [InlineData("* * * * 0-6/3")] // Every 3 days of week from (Sun-Sat) + [InlineData("* * * * MON-FRI/2")] // Every 2 weekdays from Mon-Fri + [InlineData("* * * * MON-SUN/3")] // Every 3 days from Mon-Sun + // Complex combinations with step values and ranges + [InlineData("0-30/15 8-18/2 * * *")] // Every 15 min from 0-30, every 2 hours from 8-18 + [InlineData("0-45/5 9-17/1 1-15/3 * *")] // Complex combination + [InlineData("*/10 8-18/4 1-31/7 1-12/3 MON-FRI/2")] // All fields with step values and ranges + [InlineData("1-2/3 * * * *")] // Step value larger than range public void Interval_ValidCrontab_DoesNotThrow(string crontab) { // Arrange @@ -74,14 +102,14 @@ public void Interval_SetMoreThanOnce_Throws() } [Theory] - [InlineData("")] - [InlineData("not a crontab")] - [InlineData("* * a * *")] - [InlineData("60 * * * *")] - [InlineData("* 24 * * *")] - [InlineData("* * 32 * *")] - [InlineData("* * * 13 *")] - [InlineData("* * * * 8")] + [InlineData("")] // Empty string + [InlineData("not a crontab")] // Not a valid crontab format + [InlineData("* * a * *")] // Invalid character for day-of-month + [InlineData("60 * * * *")] // Minute value exceeds 59 + [InlineData("* 24 * * *")] // Hour value exceeds 23 + [InlineData("* * 32 * *")] // Day of month value exceeds 31 + [InlineData("* * * 13 *")] // Month value exceeds 12 + [InlineData("* * * * 8")] // Day of week value exceeds 7 // Invalid step values [InlineData("*/0 * * * *")] // Step value cannot be 0 // Invalid ranges @@ -101,6 +129,28 @@ public void Interval_SetMoreThanOnce_Throws() [InlineData("*/* * * * *")] // Invalid step format [InlineData(",1,2 * * * *")] // Leading comma [InlineData("1,2, * * * *")] // Trailing comma + // Invalid step values with ranges + [InlineData("0-60/15 * * * *")] // Minute range exceeds 59 + [InlineData("0-30/0 * * * *")] // Step value cannot be 0 + [InlineData("0-30/-5 * * * *")] // Negative step value + [InlineData("* 8-25/2 * * *")] // Hour range exceeds 23 + [InlineData("* 8-18/0 * * *")] // Step value cannot be 0 + [InlineData("* * 0-31/3 * *")] // Day cannot be 0 + [InlineData("* * 1-32/3 * *")] // Day range exceeds 31 + [InlineData("* * * 0-12/2 *")] // Month cannot be 0 + [InlineData("* * * 1-13/2 *")] // Month range exceeds 12 + [InlineData("* * * * 0-8/2")] // Weekday range exceeds 7 + [InlineData("* * * * MON-FRI/0")] // Step value cannot be 0 + [InlineData("0-30//15 * * * *")] // Double slash + [InlineData("0-30/15/ * * * *")] // Trailing slash + [InlineData("0-30/15- * * * *")] // Incomplete range + // Invalid single value with step (should only allow step with * or ranges) + [InlineData("30/5 * * * *")] // Single minute with step + [InlineData("* 8/2 * * *")] // Single hour with step + [InlineData("* * 15/3 * *")] // Single day with step + [InlineData("* * * 6/2 *")] // Single month with step + [InlineData("* * * * 3/2")] // Single weekday with step + [InlineData("* * * * MON/2")] // Single weekday name with step public void CaptureCheckIn_InvalidCrontabSet_Throws(string crontab) { // Arrange