diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 0003b3d..55634bb 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -20,7 +20,11 @@ jobs: name: Build and test permissions: contents: read - runs-on: ubuntu-latest + strategy: + fail-fast: false # don't fail if one of the matrix jobs fails. Example: try to run the windows matrix even if the ubuntu matrix fails. + matrix: + os: [ubuntu-latest, windows-latest] + runs-on: ${{ matrix.os }} env: SLN_DIR: GitHubIssueFormsParser SLN_FILENAME: GitHubIssueFormsParser.sln @@ -93,7 +97,7 @@ jobs: } - name: Set run even if tests fail condition id: even-if-tests-fail - if: always() + if: matrix.os == 'ubuntu-latest' && always() run: | # Some of the steps below provide feedback on the test run and I want to run them even if # some of the previous steps failed. For that I need: @@ -109,7 +113,7 @@ jobs: Write-Output "condition is set to $condition" - name: Generate code coverage report id: code-coverage-report-generator - if: (steps.dotnet-test.conclusion == 'success' || steps.dotnet-test.conclusion == 'failure') && always() + if: matrix.os == 'ubuntu-latest' && steps.even-if-tests-fail.outputs.condition == 'true' && always() run: | $testCoverageReportDir = $(Join-Path -Path ${{ steps.dotnet-test.outputs.test-coverage-dir }} -ChildPath "report") Write-Output "test-coverage-report-dir=$testCoverageReportDir" >> $env:GITHUB_OUTPUT @@ -118,24 +122,26 @@ jobs: "-targetdir:$testCoverageReportDir" ` -reportTypes:htmlInline - name: Upload code coverage report to artifacts - if: steps.even-if-tests-fail.outputs.condition == 'true' && always() + if: matrix.os == 'ubuntu-latest' && steps.even-if-tests-fail.outputs.condition == 'true' && always() uses: actions/upload-artifact@v3 with: name: ${{ env.CODE_COVERAGE_ARTIFACT_NAME }} path: ${{ steps.code-coverage-report-generator.outputs.test-coverage-report-dir }} - name: Upload test results to artifacts - if: steps.even-if-tests-fail.outputs.condition == 'true' && always() + if: matrix.os == 'ubuntu-latest' && steps.even-if-tests-fail.outputs.condition == 'true' && always() uses: actions/upload-artifact@v3 with: name: ${{ env.TEST_RESULTS_ARTIFACT_NAME }} path: ${{ steps.dotnet-test.outputs.test-results-dir }} - name: Upload test coverage to Codecov + if: matrix.os == 'ubuntu-latest' uses: codecov/codecov-action@v3 with: token: ${{ secrets.CODECOV_TOKEN }} # even though it's not required for public repos it helps with intermittent failures caused by https://community.codecov.com/t/upload-issues-unable-to-locate-build-via-github-actions-api/3954, https://github.com/codecov/codecov-action/issues/598 files: ${{ steps.dotnet-test.outputs.test-coverage-file }} fail_ci_if_error: true - name: Log Codecov info + if: matrix.os == 'ubuntu-latest' run: | $codeCoveUrl = "https://app.codecov.io/gh/${{ github.repository }}/" Write-Output "::notice title=Code coverage (${{ runner.os }})::Code coverage has been uploaded to Codecov at $codeCoveUrl. You can download the code coverage report from the workflow artifact named: ${{ env.CODE_COVERAGE_ARTIFACT_NAME }}." diff --git a/Dockerfile b/Dockerfile index c770885..a1b1bfb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,10 @@ -#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. +# See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. +# See https://hub.docker.com/_/microsoft-dotnet-runtime/ for list of tags for dotnet runtime +# See https://hub.docker.com/_/microsoft-dotnet-sdk for list of tags for dotnet sdk -FROM mcr.microsoft.com/dotnet/runtime:7.0-alpine AS base +FROM mcr.microsoft.com/dotnet/runtime:8.0-alpine AS base # install powershell as per https://docs.microsoft.com/en-us/powershell/scripting/install/install-alpine -ARG PWSH_VERSION=7.3.6 +ARG PWSH_VERSION=7.4.0 RUN apk add --no-cache \ ca-certificates \ less \ @@ -18,14 +20,14 @@ RUN apk add --no-cache \ icu-libs \ curl RUN apk -X https://dl-cdn.alpinelinux.org/alpine/edge/main add --no-cache lttng-ust -RUN curl -L https://github.com/PowerShell/PowerShell/releases/download/v${PWSH_VERSION}/powershell-${PWSH_VERSION}-linux-alpine-x64.tar.gz -o /tmp/powershell.tar.gz +RUN curl -L https://github.com/PowerShell/PowerShell/releases/download/v${PWSH_VERSION}/powershell-${PWSH_VERSION}-linux-musl-x64.tar.gz -o /tmp/powershell.tar.gz RUN mkdir -p /opt/microsoft/powershell/7 RUN tar zxf /tmp/powershell.tar.gz -C /opt/microsoft/powershell/7 RUN chmod +x /opt/microsoft/powershell/7/pwsh RUN ln -s /opt/microsoft/powershell/7/pwsh /usr/bin/pwsh # end of install powershell -FROM mcr.microsoft.com/dotnet/sdk:7.0-alpine AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build WORKDIR /github-issue-forms-parser COPY ["GitHubIssueFormsParser/NuGet.Config", "GitHubIssueFormsParser/"] COPY ["GitHubIssueFormsParser/src/GitHubIssuesParserCli/GitHubIssuesParserCli.csproj", "GitHubIssueFormsParser/src/GitHubIssuesParserCli/"] diff --git a/GitHubIssueFormsParser/global.json b/GitHubIssueFormsParser/global.json index 90e17e6..1a11256 100644 --- a/GitHubIssueFormsParser/global.json +++ b/GitHubIssueFormsParser/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.x", + "version": "8.0.x", "rollForward": "disable", "allowPrerelease": false } diff --git a/GitHubIssueFormsParser/src/GitHubIssuesParserCli/GitHubIssuesParserCli.csproj b/GitHubIssueFormsParser/src/GitHubIssuesParserCli/GitHubIssuesParserCli.csproj index 3eddfe9..abd2785 100644 --- a/GitHubIssueFormsParser/src/GitHubIssuesParserCli/GitHubIssuesParserCli.csproj +++ b/GitHubIssueFormsParser/src/GitHubIssuesParserCli/GitHubIssuesParserCli.csproj @@ -2,7 +2,7 @@ Exe - net7.0 + net8.0 diff --git a/GitHubIssueFormsParser/src/GitHubIssuesParserCli/IssueFormBodies/IssueFormBody.cs b/GitHubIssueFormsParser/src/GitHubIssuesParserCli/IssueFormBodies/IssueFormBody.cs index 3c886a6..09ad4e0 100644 --- a/GitHubIssueFormsParser/src/GitHubIssuesParserCli/IssueFormBodies/IssueFormBody.cs +++ b/GitHubIssueFormsParser/src/GitHubIssuesParserCli/IssueFormBodies/IssueFormBody.cs @@ -2,6 +2,12 @@ namespace GitHubIssuesParserCli.IssueFormBodies; internal sealed class IssueFormBody { + private static readonly JsonSerializerOptions _serializeOptions = new JsonSerializerOptions + { + WriteIndented = false, + Converters = { new IssueFormBodyJsonConverter() }, + }; + public IssueFormBody(List items) { Items = items.NotNull(); @@ -11,15 +17,7 @@ public IssueFormBody(List items) public string ToJson() { - var serializeOptions = new JsonSerializerOptions - { - WriteIndented = false, - Converters = - { - new IssueFormBodyJsonConverter(), - }, - }; - return JsonSerializer.Serialize(this, serializeOptions); + return JsonSerializer.Serialize(this, _serializeOptions); } public void WriteAsJson(Utf8JsonWriter writer) diff --git a/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/Auxiliary/LineEndings.cs b/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/Auxiliary/LineEndings.cs index 37b5fce..d7c0166 100644 --- a/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/Auxiliary/LineEndings.cs +++ b/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/Auxiliary/LineEndings.cs @@ -1,7 +1,17 @@ namespace GitHubIssuesParserCli.Tests.Auxiliary; -internal static class LineEndings +internal static partial class LineEndings { + // matchTimeoutMilliseconds used to prevent denial of service attacks. See MA0009 https://github.com/meziantou/Meziantou.Analyzer/blob/main/docs/Rules/MA0009.md + // Adding 5 secs which for this scenario means: a random high enough value. It will probably work with much lower values but + // for this code this doesn't matter much as long as it's compliant with MA0009. + [GeneratedRegex(@"\r\n", RegexOptions.IgnoreCase, matchTimeoutMilliseconds: 5000)] + private static partial Regex WindowsNewLinesRegex(); + + // This pattern uses a negative lookbehind to ensure that the LF character is not preceded by a CR character. + [GeneratedRegex(@"(? /// Updates a string so that the line endings match the OS expected line endings. /// @@ -9,24 +19,18 @@ internal static class LineEndings /// A string containing line endings matching the OS expected line endings. public static string NormalizeLineEndings(this string original) { - if (Environment.OSVersion.Platform == PlatformID.Win32NT && original.Contains(CR + LF, StringComparison.Ordinal)) - { - // if it's a Windows OS and contains Windows line endings then do nothing - return original; - } - - if (Environment.OSVersion.Platform == PlatformID.Win32NT && original.Contains(LF, StringComparison.Ordinal)) + if (Environment.OSVersion.Platform == PlatformID.Win32NT && LinuxNewLinesRegex().IsMatch(original)) { // if it's a Windows OS and doesn't contain Windows line endings then replace // new lines with Windows line endings - return original.Replace(LF, Environment.NewLine, StringComparison.Ordinal); + return LinuxNewLinesRegex().Replace(original, CR + LF); } - if (Environment.OSVersion.Platform == PlatformID.Unix && original.Contains(CR + LF, StringComparison.Ordinal)) + if (Environment.OSVersion.Platform == PlatformID.Unix && WindowsNewLinesRegex().IsMatch(original)) { // if it's a Linux OS and contains Windows line endings then replace // new lines with Linux line endings - return original.Replace(CR + LF, Environment.NewLine, StringComparison.Ordinal); + return WindowsNewLinesRegex().Replace(original, LF); } return original; diff --git a/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/GitHubIssuesParserCli.Tests.csproj b/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/GitHubIssuesParserCli.Tests.csproj index 989f272..1e94cb9 100644 --- a/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/GitHubIssuesParserCli.Tests.csproj +++ b/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/GitHubIssuesParserCli.Tests.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 false true diff --git a/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/Usings.cs b/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/Usings.cs index 7c55555..6de08cb 100644 --- a/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/Usings.cs +++ b/GitHubIssueFormsParser/tests/GitHubIssuesParserCli.Tests/Usings.cs @@ -1,5 +1,6 @@ global using System.Text.Json; global using System.Text.Json.Serialization; +global using System.Text.RegularExpressions; global using CliFx.Exceptions; global using CliFx.Extensibility; global using CliFx.Infrastructure;