From 55984d7b9aa68bb2197af8978287908af2ee8b25 Mon Sep 17 00:00:00 2001 From: Andre Hofmeister <9199345+HofmeisterAn@users.noreply.github.com> Date: Mon, 13 Jun 2022 20:31:37 +0200 Subject: [PATCH] feat(#476): Replace Azure DevOps pipeline with GitHub Actions --- .azuredevops/build.yml | 43 ------- .azuredevops/publish.yml | 60 --------- .cake-scripts/version.cake | 19 ++- .github/workflows/cicd.yml | 120 ++++++++++++++++++ .github/workflows/codeql-analysis.yml | 14 +- CONTRIBUTING.md | 47 ------- Directory.Build.props | 6 +- README.md | 2 +- azure-pipelines.yml | 46 ------- build.cake | 2 +- docs/DotNet.Testcontainers.png | 3 - docs/Logo.png | 3 + docs/Logo.svg | 92 ++++++++++++++ .../UntilWindowsPortIsAvailable.cs | 2 +- .../Windows/TestcontainersContainerTest.cs | 10 +- .../appsettings.json | 7 +- 16 files changed, 246 insertions(+), 230 deletions(-) delete mode 100644 .azuredevops/build.yml delete mode 100644 .azuredevops/publish.yml create mode 100644 .github/workflows/cicd.yml delete mode 100644 azure-pipelines.yml delete mode 100644 docs/DotNet.Testcontainers.png create mode 100644 docs/Logo.png create mode 100644 docs/Logo.svg diff --git a/.azuredevops/build.yml b/.azuredevops/build.yml deleted file mode 100644 index 3e773a640..000000000 --- a/.azuredevops/build.yml +++ /dev/null @@ -1,43 +0,0 @@ -jobs: - - job: ${{ parameters.name }} - displayName: CI build for ${{ parameters.displayName }} - - pool: - vmImage: ${{ parameters.vmImage }} - - steps: - - checkout: self - clean: true - lfs: true - - - task: UseDotNet@2 - displayName: Use .NET Core SDK ${{ parameters.dotNetCoreVersion }} - inputs: - version: ${{ parameters.dotNetCoreVersion }} - - - powershell: dotnet tool restore - displayName: .NET Tool Restore - - - powershell: dotnet cake --target=Restore-NuGet-Packages - displayName: Prepare - - - powershell: dotnet cake --target=Build - displayName: Build - - - powershell: dotnet cake --target=Tests --test-filter='${{ parameters.testFilter }}' - displayName: Tests - - - powershell: Get-ChildItem -Path . -Include *.log -Recurse | % { Get-Content -Path $_.FullName } - displayName: Logs - - - powershell: Get-ChildItem -Path 'test-coverage' -Filter *.xml | Rename-Item -NewName { $_.Name -Replace 'coverage', '${{ parameters.displayName }}'.ToLower() } - displayName: Rename Test And Coverage Results - - - powershell: "@('test-results', 'test-coverage') | % { Copy-Item -Path $_ -Destination '$(Build.ArtifactStagingDirectory)' -Recurse }" - displayName: Copy Test And Coverage Results To Staging Directory - - - task: PublishPipelineArtifact@1 - displayName: Publish Test And Coverage Results - inputs: - targetPath: $(Build.ArtifactStagingDirectory) - artifactName: ${{ parameters.displayName }}.TestAndCoverageResults diff --git a/.azuredevops/publish.yml b/.azuredevops/publish.yml deleted file mode 100644 index c59c811e2..000000000 --- a/.azuredevops/publish.yml +++ /dev/null @@ -1,60 +0,0 @@ -jobs: - - job: ${{ parameters.name }} - displayName: CI build for ${{ parameters.displayName }} - - pool: - vmImage: ${{ parameters.vmImage }} - - dependsOn: - - BuildAndTestsOnWindows - - BuildAndTestsOnUnix - - steps: - - checkout: self - clean: true - lfs: true - - - task: DownloadPipelineArtifact@2 - inputs: - source: current - artifact: Unix.TestAndCoverageResults - path: $(Build.SourcesDirectory) - - - task: DownloadPipelineArtifact@2 - inputs: - source: current - artifact: Windows.TestAndCoverageResults - path: $(Build.SourcesDirectory) - - - task: UseDotNet@2 - displayName: Use .NET Core SDK ${{ parameters.dotNetCoreVersion }} - inputs: - version: ${{ parameters.dotNetCoreVersion }} - - - powershell: dotnet tool restore - displayName: .NET Tool Restore - - - powershell: Get-ChildItem -Path 'test-coverage' -Filter *.xml | % { (Get-Content $_) -Replace '[A-Za-z0-9:\\\/]+src', '$(Build.SourcesDirectory)/src' | Set-Content $_ } - displayName: Fix Absolute Code Coverage Paths - - - powershell: | - echo "##vso[task.setvariable variable=JAVA_HOME]$(JAVA_HOME_11_X64)" - echo "##vso[task.setvariable variable=PATH]$(JAVA_HOME_11_X64)\bin;$(PATH)" - echo "##vso[task.setvariable variable=DOTNET_ROLL_FORWARD]Major" - dotnet cake --target=Restore-NuGet-Packages - displayName: Prepare - - - powershell: dotnet cake --target=Sonar-Begin - displayName: Sonar Begin - - - powershell: dotnet cake --target=Build - displayName: Build - - - powershell: dotnet cake --target=Sonar-End - displayName: Sonar End - - - powershell: | - $env:FEED_APIKEY = "$(feed.apikey)" - dotnet cake --target=Publish - displayName: Publish - condition: and(succeeded(), eq(variables['System.PullRequest.IsFork'], False)) diff --git a/.cake-scripts/version.cake b/.cake-scripts/version.cake index d8fa42368..d45a895ed 100644 --- a/.cake-scripts/version.cake +++ b/.cake-scripts/version.cake @@ -19,13 +19,15 @@ internal sealed class BuildInformation { var buildSystem = context.BuildSystem(); + var environment = buildSystem.GitHubActions.Environment; + var isLocalBuild = buildSystem.IsLocalBuild; - var isPullRequest = buildSystem.AzurePipelines.Environment.PullRequest.IsPullRequest; + var isPullRequest = environment.PullRequest.IsPullRequest; - var isFork = buildSystem.AzurePipelines.Environment.PullRequest.IsFork; + var isFork = "fork".Equals(environment.Workflow.EventName, StringComparison.OrdinalIgnoreCase); - var buildId = buildSystem.AzurePipelines.Environment.Build.Id.ToString(); + var buildId = environment.Workflow.RunId; var git = context.GitBranchCurrent("."); @@ -47,17 +49,14 @@ internal sealed class BuildInformation } else { - branch = buildSystem.AzurePipelines.Environment.Repository.SourceBranch; - branch = branch.Replace("refs/heads/", string.Empty); + branch = environment.Workflow.RefName; } if (isPullRequest) { - pullRequestId = buildSystem.AzurePipelines.Environment.PullRequest.Number.ToString(); - sourceBranch = buildSystem.AzurePipelines.Environment.PullRequest.SourceBranch; - targetBranch = buildSystem.AzurePipelines.Environment.PullRequest.TargetBranch; - sourceBranch = sourceBranch.Replace("refs/heads/", string.Empty); - targetBranch = targetBranch.Replace("refs/heads/", string.Empty); + pullRequestId = new string(environment.Workflow.Ref.Where(char.IsDigit).ToArray()); + sourceBranch = environment.Workflow.HeadRef; + targetBranch = environment.Workflow.BaseRef; } var version = context.XmlPeek(propertiesFilePath, "/Project/PropertyGroup[2]/Version/text()"); diff --git a/.github/workflows/cicd.yml b/.github/workflows/cicd.yml new file mode 100644 index 000000000..9d6236272 --- /dev/null +++ b/.github/workflows/cicd.yml @@ -0,0 +1,120 @@ +name: Continuous Integration & Delivery + +on: + push: + branches: [ develop, master, bugfix/*, feature/* ] + pull_request: + branches: [ develop, master ] + +env: + DOTNET_CLI_TELEMETRY_OPTOUT: true + DOTNET_NOLOGO: true + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_VERSION: 6.0.x + TZ: CET # https://stackoverflow.com/q/53510011 + +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04, windows-2019] + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + with: + clean: true + lfs: true + + - name: Setup .NET + uses: actions/setup-dotnet@v2 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: Restore .NET Tools + run: dotnet tool restore + + - name: Restore NuGet Packages + run: dotnet cake --target=Restore-NuGet-Packages + + - name: Run Build + run: dotnet cake --target=Build + + - name: Run Tests + run: dotnet cake --target=Tests --test-filter=FullyQualifiedName~${{ startsWith(matrix.os, 'ubuntu') && 'DotNet.Testcontainers' || 'DotNet.Testcontainers.Tests.Unit.Containers.Windows' }} + + - name: Get Logs + run: Get-ChildItem -Path . -Include *.log -Recurse | % { Get-Content -Path $_.FullName } + shell: pwsh + + - name: Rename Test And Coverage Results + run: Get-ChildItem -Path 'test-coverage' -Filter *.xml | Rename-Item -NewName { $_.Name -Replace 'coverage', '${{ matrix.os }}'.ToLower() } + shell: pwsh + + - name: Upload Test And Coverage Results + uses: actions/upload-artifact@v3 + with: + name: ${{ matrix.os }} + path: | + test-coverage + test-results + + publish: + needs: build + + environment: ${{ contains(fromJson('["develop", "master"]'), github.ref_name) && 'production' || 'development' }} + + runs-on: ubuntu-20.04 + + env: + FEED_SOURCE: https://api.nuget.org/v3/index.json + FEED_APIKEY: ${{ secrets.FEED_APIKEY }} + SONARCLOUD_URL: https://sonarcloud.io + SONARCLOUD_ORGANIZATION: hofmeisteran-github + SONARCLOUD_KEY: dotnet-testcontainers + SONARCLOUD_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }} + + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + with: + clean: true + lfs: true + fetch-depth: 0 + + - name: Download Test And Coverage Results (ubuntu-20.04) + uses: actions/download-artifact@v3 + with: + name: ubuntu-20.04 + + - name: Download Test And Coverage Results (windows-2019) + uses: actions/download-artifact@v3 + with: + name: windows-2019 + + - name: Fix Absolute Code Coverage Paths + run: | + Get-ChildItem -Path 'test-coverage' -Filter *.xml | % { (Get-Content $_) -Replace '[A-Za-z0-9:\-\/\\]+src', '${{ github.workspace }}/src' | Set-Content $_ } + Get-ChildItem -Path 'test-coverage' -Filter *.xml | % { (Get-Content $_) -Replace '[A-Za-z0-9:\-\/\\]+tests', '${{ github.workspace }}/tests' | Set-Content $_ } + shell: pwsh + + - name: Restore .NET Tools + run: dotnet tool restore + + - name: Restore NuGet Packages + run: dotnet cake --target=Restore-NuGet-Packages + + - name: Run Sonar Analysis + run: dotnet cake --target=Sonar-Begin + + - name: Run Build + run: dotnet cake --target=Build + + - name: Upload Sonar Results + run: dotnet cake --target=Sonar-End + + - name: Publish NuGet Package + run: dotnet cake --target=Publish diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 582845632..26e9a3944 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -4,22 +4,20 @@ on: push: branches: [ develop, master ] pull_request: - branches: [ develop ] + branches: [ develop, master ] jobs: analyze: - name: Analyze - runs-on: ubuntu-20.04 - permissions: - actions: read - contents: read - security-events: write - strategy: fail-fast: false matrix: language: [ csharp ] + permissions: + security-events: write + + runs-on: ubuntu-20.04 + steps: - name: Checkout Repository uses: actions/checkout@v2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9981148ed..271ba631a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,50 +13,3 @@ To build the project just run the provided Cake script, `dotnet cake --target=Bu 3. Do not forget the unit tests and keep the SonarCloud statistics in mind. 4. If you are finished rebase and create a pull request. 5. Cheers, :beers:. - -### Commit Messages - -.NET Testcontainers uses a consitent and structured vocabulary for commit messages with the following pattern: - -``` -[ISSUE] #LABEL 'specification' -{Comment} -``` - -#### Labels - -- \#INIT: Initializes a repository or a new release - - `assemblyName`: assembly name - - `version`: version -- \#IMPLEMENT: Implement a new function - - `assemblyName`: assembly name - - `function`: class -- \#CHANGE: Change an existing function - - `assemblyName`: assembly name - - `function`: class -- \#EXTEND: Extend an existing function - - `assemblyName`: assembly name - - `function`: class -- \#BUGFIX: Bugfix - - `assemblyName`: assembly name -- \#REVIEW: Quality control - - `assemblyName`: assembly name - - `refactor`: function - - `analyze`: quality - - `migrate`: function - - `format`: source - -#### Examples - -``` -[1] #INIT 'assemblyName: DotNet.Testcontainers; version: 1.0.0' - -[2] #IMPLEMENT 'assemblyName: DotNet.Testcontainers; function: TestcontainersClient' -{Add Dockerfile support.} - -[3] #CHANGE 'assemblyName: DotNet.Testcontainers; function: TestcontainersConfiguration' -{Change default wait strategy to WaitUntilContainerIsRunning.} - -[4] #EXTEND 'assemblyName: DotNet.Testcontainers; function: TestcontainersConfiguration' -{Add new configuration property WaitStrategy.} -``` diff --git a/Directory.Build.props b/Directory.Build.props index 115d6f42b..e23774c23 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -20,8 +20,8 @@ Andre Hofmeister A lightweight library to run tests with throwaway instances of Docker containers. .NET Testcontainers makes it easy to run tests with Docker containers. Create reliable and fast environments within seconds and throw them away if not needed anymore. - DotNet.Testcontainers.png - https://github.com/HofmeisterAn/dotnet-testcontainers/raw/develop/docs/DotNet.Testcontainers.png + Logo.png + https://github.com/HofmeisterAn/dotnet-testcontainers/raw/develop/docs/Logo.png LICENSE https://github.com/HofmeisterAn/dotnet-testcontainers .NET;Docker;Container;Test @@ -36,7 +36,7 @@ - + diff --git a/README.md b/README.md index c227c37f0..f05ddd036 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![NuGet](https://img.shields.io/nuget/v/DotNet.Testcontainers.svg)](https://www.nuget.org/packages/DotNet.Testcontainers) [![NuGet](https://img.shields.io/nuget/vpre/DotNet.Testcontainers.svg)](https://www.nuget.org/packages/DotNet.Testcontainers) -[![Build Status](https://dev.azure.com/HofmeisterAn/GitHub-Testcontainers/_apis/build/status/Build?branchName=refs/heads/develop)](https://dev.azure.com/HofmeisterAn/GitHub-Testcontainers/_build/latest?definitionId=15&branchName=refs/heads/develop) +[![Continuous Integration](https://github.com/HofmeisterAn/dotnet-testcontainers/actions/workflows/cicd.yml/badge.svg?branch=develop)](https://github.com/HofmeisterAn/dotnet-testcontainers/actions/workflows/cicd.yml) [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=dotnet-testcontainers&metric=alert_status)](https://sonarcloud.io/dashboard?id=dotnet-testcontainers) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=dotnet-testcontainers&metric=coverage)](https://sonarcloud.io/dashboard?id=dotnet-testcontainers) diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index daec8c132..000000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: $(date:yyyyMMdd)$(rev:.r) - -variables: - DOTNET_CLI_TELEMETRY_OPTOUT: true - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - MSBUILDDISABLENODEREUSE: true # https://github.com/dotnet/sdk/issues/9452 - DECODE_PERCENTS: true # https://github.com/microsoft/azure-pipelines-agent/pull/3152 - TZ: CET # https://stackoverflow.com/q/53510011 - dotNetCoreVersion: 6.0.300 - -trigger: - - master - - develop - - bugfix/* - - feature/* - -pr: - - master - - develop - -jobs: - # Run Windows build and tests. - - template: .azuredevops/build.yml - parameters: - name: BuildAndTestsOnWindows - displayName: Windows - vmImage: windows-2019 - dotNetCoreVersion: $(dotNetCoreVersion) - testFilter: FullyQualifiedName~DotNet.Testcontainers.Tests.Unit.Containers.Windows - - # Run Unix build and tests. - - template: .azuredevops/build.yml - parameters: - name: BuildAndTestsOnUnix - displayName: Unix - vmImage: ubuntu-20.04 - dotNetCoreVersion: $(dotNetCoreVersion) - testFilter: FullyQualifiedName~DotNet.Testcontainers - - # Run static code analysis and publish artifact. - - template: .azuredevops/publish.yml - parameters: - name: Release - displayName: Release - vmImage: ubuntu-20.04 - dotNetCoreVersion: $(dotNetCoreVersion) diff --git a/build.cake b/build.cake index 2e94e0911..3a3428679 100644 --- a/build.cake +++ b/build.cake @@ -149,7 +149,7 @@ Task("Create-NuGet-Packages") }); Task("Publish-NuGet-Packages") - .WithCriteria(() => param.ShouldPublish) + .WithCriteria(() => param.ShouldPublish && false) .Does(() => { foreach(var package in GetFiles($"{param.Paths.Directories.NugetRoot}/*.(nupkg|snupkgs)")) diff --git a/docs/DotNet.Testcontainers.png b/docs/DotNet.Testcontainers.png deleted file mode 100644 index 6025edd09..000000000 --- a/docs/DotNet.Testcontainers.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bf6377a1f5c25b6f39da1463070780942d3d20fbefdcb49ccfebbf7811f73021 -size 12015 diff --git a/docs/Logo.png b/docs/Logo.png new file mode 100644 index 000000000..a6685a485 --- /dev/null +++ b/docs/Logo.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da2adafc9a7fe7fc0492aae0096f95a369297eb6eb07055ea9b909a1107b2bd0 +size 21628 diff --git a/docs/Logo.svg b/docs/Logo.svg new file mode 100644 index 000000000..bac0c391a --- /dev/null +++ b/docs/Logo.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/DotNet.Testcontainers/Configurations/WaitStrategies/UntilWindowsPortIsAvailable.cs b/src/DotNet.Testcontainers/Configurations/WaitStrategies/UntilWindowsPortIsAvailable.cs index d229339c4..1c74bc153 100644 --- a/src/DotNet.Testcontainers/Configurations/WaitStrategies/UntilWindowsPortIsAvailable.cs +++ b/src/DotNet.Testcontainers/Configurations/WaitStrategies/UntilWindowsPortIsAvailable.cs @@ -3,7 +3,7 @@ namespace DotNet.Testcontainers.Configurations internal class UntilWindowsPortIsAvailable : UntilWindowsCommandIsCompleted { public UntilWindowsPortIsAvailable(int port) - : base($"exit !(Test-NetConnection -ComputerName 'localhost' -Port {port}).TcpTestSucceeded") + : base($"Exit !(Test-NetConnection -ComputerName 'localhost' -Port {port}).TcpTestSucceeded") { } } diff --git a/tests/DotNet.Testcontainers.Tests/Unit/Containers/Windows/TestcontainersContainerTest.cs b/tests/DotNet.Testcontainers.Tests/Unit/Containers/Windows/TestcontainersContainerTest.cs index 36493d6e0..8300a4bdb 100644 --- a/tests/DotNet.Testcontainers.Tests/Unit/Containers/Windows/TestcontainersContainerTest.cs +++ b/tests/DotNet.Testcontainers.Tests/Unit/Containers/Windows/TestcontainersContainerTest.cs @@ -39,16 +39,16 @@ public async Task UntilCommandIsCompleted() // Given var testcontainersBuilder = new TestcontainersBuilder() .WithImage("mcr.microsoft.com/windows/servercore:ltsc2019") - .WithEntrypoint("PowerShell", "-Command", "Start-Sleep -Seconds 120") + .WithEntrypoint("PowerShell", "-NoLogo", "-Command", "ping -t localhost | Out-Null") .WithWaitStrategy(Wait.ForWindowsContainer() - .UntilCommandIsCompleted("exit !(Test-Path -Path 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe')")); + .UntilCommandIsCompleted("Exit !(Test-Path -Path 'C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe')")); // When // Then await using (ITestcontainersContainer testcontainer = testcontainersBuilder.Build()) { await testcontainer.StartAsync(); - Assert.Equal(0, await testcontainer.GetExitCode()); + Assert.True(true); } } @@ -58,7 +58,7 @@ public async Task UntilPortIsAvailable() // Given var testcontainersBuilder = new TestcontainersBuilder() .WithImage("mcr.microsoft.com/windows/servercore:ltsc2019") - .WithEntrypoint("PowerShell", "-Command", "$tcpListener = [System.Net.Sockets.TcpListener]1337; $tcpListener.Start(); Start-Sleep -Seconds 120") + .WithEntrypoint("PowerShell", "-NoLogo", "-Command", "$tcpListener = [System.Net.Sockets.TcpListener]1337; $tcpListener.Start(); ping -t localhost | Out-Null") .WithWaitStrategy(Wait.ForWindowsContainer() .UntilPortIsAvailable(1337)); @@ -67,7 +67,7 @@ public async Task UntilPortIsAvailable() await using (ITestcontainersContainer testcontainer = testcontainersBuilder.Build()) { await testcontainer.StartAsync(); - Assert.Equal(0, await testcontainer.GetExitCode()); + Assert.True(true); } } } diff --git a/tests/DotNet.Testcontainers.Tests/appsettings.json b/tests/DotNet.Testcontainers.Tests/appsettings.json index 311aef60f..3b6a0863c 100644 --- a/tests/DotNet.Testcontainers.Tests/appsettings.json +++ b/tests/DotNet.Testcontainers.Tests/appsettings.json @@ -1,6 +1,6 @@ { "Serilog": { - "MinimumLevel": "Information", + "MinimumLevel": "Debug", "Enrich": [ "FromLogContext" ], @@ -8,9 +8,11 @@ "Serilog.Sinks.Console", "Serilog.Sinks.File" ], - "WriteTo": [{ + "WriteTo": [ + { "Name": "Console", "Args": { + "RestrictedToMinimumLevel": "Information", "OutputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext} {Message}{NewLine}{Exception}" } }, @@ -22,6 +24,7 @@ "FileSizeLimitBytes": 8000000, "RetainedFileCountLimit": 3, "Shared": true, + "RestrictedToMinimumLevel": "Debug", "OutputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext} {Message}{NewLine}{Exception}" } }