diff --git a/.azure/pipelines/ci.yml b/.azure/pipelines/ci.yml new file mode 100644 index 0000000..a22b4d2 --- /dev/null +++ b/.azure/pipelines/ci.yml @@ -0,0 +1,47 @@ +variables: + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + CI_BUILD_NUMBER: $(Build.BuildId) + BRANCH_NAME: $(Build.SourceBranchName) + TAG_NAME: $(Build.SourceBranchName) + +trigger: + - master + - ci-* + - refs/tags/v* + +pr: + branches: + include: + - master + +#schedules: +# - cron: "0 0 * * *" +# displayName: "Daily midnight build" +# branches: +# include: +# - master + +stages: + - stage: Build_Test + jobs: + - template: jobs/build_and_test.yml + + - stage: E2E_Tests + dependsOn: + - Build_Test + jobs: + - template: jobs/e2e_tests.yml + + - stage: Code_Coverage + dependsOn: + - Build_Test + jobs: + - template: jobs/coverage.yml + + - stage: Deploy + dependsOn: + - E2E_Tests + condition: and( succeeded(), startsWith( variables['Build.SourceBranch'], 'refs/tags' ) ) + jobs: + - template: jobs/deploy_nuget.yml diff --git a/.azure/pipelines/jobs/build_and_test.yml b/.azure/pipelines/jobs/build_and_test.yml new file mode 100644 index 0000000..d53632f --- /dev/null +++ b/.azure/pipelines/jobs/build_and_test.yml @@ -0,0 +1,70 @@ +jobs: + - job: build_test + displayName: build and test + pool: + vmImage: 'ubuntu-18.04' + strategy: + matrix: + debug-build: + BUILD_CONFIG: Debug + release-build: + BUILD_CONFIG: Release + steps: + - bash: | + echo 'installed sdks:' + dotnet --list-sdks + echo "-------------------------------------------------" + echo 'environment variables:' + env | sort + + chmod u+x ./build.sh + displayName: init + + - bash: ./build.sh build + displayName: build + + # coverlet.msbuild must be added as package to the tests + - bash: ./build.sh test-coverage + displayName: test + + - task: PublishTestResults@2 + condition: always() + inputs: + testRunner: VSTest + # no global glob, because of trx-test-files used in the project + testResultsFiles: 'tests/trx2junit.Tests/TestResults/*.trx' + + - bash: | + cd tests/Coverage + + workDir="$(System.DefaultWorkingDirectory)" + + if [[ "$AGENT_OS" == "Windows_NT" ]]; then + # Windows needs special treetment for the substitution due the \ + workDir="${workDir//\\/\\\\}\\\\" + else + workDir+="/" + fi + + # Mac has a different sed, so special case it (hooray for standars ;-)) + if [[ "$AGENT_OS" != "Darwin" ]]; then + sed -i 's|[^<]*|/|g' "Cobertura.xml" + sed -i "s|${workDir}||g" "Cobertura.xml" + else + sed -i '' 's|[^<]+|/|g' "Cobertura.xml" + sed -i '' "s|${workDir}||g" "Cobertura.xml" + fi + displayName: make Cobertura-paths relative + + # shortcut for PublishPipelineArtifact + # Coverage report will be created later in a different stage + - publish: tests/Coverage/Cobertura.xml + artifact: 'Coverage-${{ parameters.name }}-$(BUILD_CONFIG)' + + - bash: ./build.sh pack + displayName: pack + condition: and(succeeded(), eq(variables['BUILD_CONFIG'], 'Release')) + + - publish: 'NuGet-Packed' + artifact: 'NuGet-Packed' + condition: and(succeeded(), eq(variables['BUILD_CONFIG'], 'Release')) diff --git a/.azure/pipelines/jobs/coverage.yml b/.azure/pipelines/jobs/coverage.yml new file mode 100644 index 0000000..f72bd8c --- /dev/null +++ b/.azure/pipelines/jobs/coverage.yml @@ -0,0 +1,28 @@ +jobs: + - job: coverage_report_quality + pool: + vmImage: 'ubuntu-18.04' + steps: + # checkout is needed for file-references + #- checkout: none + + - task: DownloadPipelineArtifact@2 + inputs: + targetPath: './coverage' + + - task: PublishCodeCoverageResults@1 + inputs: + codeCoverageTool: Cobertura + summaryFileLocation: 'coverage/**/Cobertura.xml' + pathToSources: $(System.DefaultWorkingDirectory) + + # extension from Marketplace https://marketplace.visualstudio.com/acquisition?itemName=mspremier.BuildQualityChecks + - task: BuildQualityChecks@6 + displayName: 'Check build quality' + inputs: + checkCoverage: true + coverageFailOption: 'build' + coverageType: 'lines' + allowCoverageVariance: true + coverageVariance: '0.5' + baseBranchRef: 'master' diff --git a/.azure/pipelines/jobs/deploy_nuget.yml b/.azure/pipelines/jobs/deploy_nuget.yml new file mode 100644 index 0000000..98eb18a --- /dev/null +++ b/.azure/pipelines/jobs/deploy_nuget.yml @@ -0,0 +1,66 @@ +jobs: + - job: deploy_nuget + pool: + vmImage: 'ubuntu-18.04' + steps: + - checkout: none + + - task: DownloadPipelineArtifact@2 + inputs: + artifactName: 'NuGet-Packed' + targetPath: './' + + - bash: | + echo "NuGet-Packed:" + ls -la . + + if [[ "$TAG_NAME" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)(-(preview-[0-9]+))$ ]]; then + mkdir deploy_custom + + for package in ./*.nupkg; do + mv $package deploy_custom + done + + echo "##vso[task.setvariable variable=deploy_custom;]1" + elif [[ "$TAG_NAME" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)$ ]]; then + mkdir deploy_nuget + + for package in ./*.nupkg; do + mv $package deploy_nuget + done + + echo "##vso[task.setvariable variable=deploy_nuget;]1" + else + echo "no deploy, as $TAG_NAME does not match" + echo ##vso[task.complete result=Skipped;]tag does not match for deploy + fi + + echo "-------------------------------------------------" + echo "custom:" + ls -la deploy_custom + echo "-------------------------------------------------" + echo "nuget:" + ls -la deploy_nuget + echo "-------------------------------------------------" + displayName: 'deploy to nuget / custom' + + - task: NuGetAuthenticate@0 + condition: eq(variables['deploy_custom'], '1') + + - task: NuGetCommand@2 + condition: eq(variables['deploy_custom'], '1') + inputs: + command: push + packagesToPush: deploy_custom/*.nupkg + nuGetFeedType: 'internal' + publishVstsFeed: 'github-Projects/gfoidl-public' + displayName: deploy to custom feed + + - task: NuGetCommand@2 + condition: eq(variables['deploy_nuget'], '1') + inputs: + command: push + packagesToPush: deploy_nuget/*.nupkg + nuGetFeedType: external + publishFeedCredentials: 'nuget - gfoidl' + displayName: deploy to nuget diff --git a/.azure/pipelines/jobs/e2e_tests.yml b/.azure/pipelines/jobs/e2e_tests.yml new file mode 100644 index 0000000..5ce2d68 --- /dev/null +++ b/.azure/pipelines/jobs/e2e_tests.yml @@ -0,0 +1,82 @@ +jobs: + - job: e2e_tests + displayName: e2e tests + pool: + vmImage: 'ubuntu-18.04' + steps: + - bash: | + sudo apt update + sudo apt install -y libxml2-utils + displayName: install xml-lint + + - task: DownloadPipelineArtifact@2 + inputs: + artifactName: 'NuGet-Packed' + targetPath: './NuGet-Packed' + + - bash: | + chmod ugo+x -R *.sh + chmod ugo+x ./tests/scripts/*.sh + displayName: init + + - bash: | + # copied from build.sh (but modified) + if [[ -n "$TAG_NAME" ]]; then + if [[ "$TAG_NAME" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)(-(preview-[0-9]+))?$ ]]; then + export VersionMajor="${BASH_REMATCH[1]}" + export VersionMinor="${BASH_REMATCH[2]}" + export VersionPatch="${BASH_REMATCH[3]}" + export VersionSuffix="${BASH_REMATCH[5]}" + + ToolVersion="$VersionMajor.$VersionMinor.$VersionPatch-$VersionSuffix" + fi + fi + + # special handling for pre-releases (is a constraint by .NET Core global tools) + # and also to prevent installation from NuGet-feed (which may have higher version than the + # built tool) + if [[ -z "$ToolVersion" ]]; then + dotnet tool install --tool-path $(pwd)/tool --configfile=ci-nuget.config trx2junit + else + dotnet tool install --tool-path $(pwd)/tool --version="$ToolVersion" --configfile=ci-nuget.config trx2junit + fi + + echo "##vso[task.prependpath]$(pwd)/tool" + displayName: install built trx2junit-tool + + - bash: | + echo $PATH + echo "-------------------------------------------------" + dotnet tool list --tool-path $(pwd)/tool + echo "-------------------------------------------------" + trx2junit + + if [[ $? != 1 ]]; then + echo "hm, something strange" + exit 1 + fi + displayName: check tool installation + + - bash: ./build.sh build + displayName: build + + - bash: ./tests/scripts/run-samples.sh + displayName: samples + + - bash: ./tests/scripts/run-single-arg.sh + displayName: single-arg + + - bash: ./tests/scripts/run-multiple-args.sh + displayName: multiple-args + + - bash: ./tests/scripts/run-globbing.sh + displayName: globbing + + - bash: ./tests/scripts/run-no-globbing.sh + displayName: no-globbing + + - bash: ./tests/scripts/run-different-output-location.sh + displayName: different-output-location + + - bash: ./tests/scripts/run-junit2trx.sh + displayName: junit2trx diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 7cb85b0..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,196 +0,0 @@ -version: 2.1 - -executors: - build-executor: - docker: - - image: gfoidl/dotnet-xmllint:3.0 - environment: - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - - deploy-executor: - docker: - - image: mcr.microsoft.com/dotnet/core/sdk:3.0 - environment: - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1 - DOTNET_CLI_TELEMETRY_OPTOUT: 1 - -commands: - set_env: - description: "Sets the build environment variables" - steps: - - run: - name: "set build environment" - command: | - if [[ -z "$CI_BUILD_NUMBER" ]]; then - echo 'export CI_BUILD_NUMBER="$CIRCLE_BUILD_NUM"' >> "$BASH_ENV" - echo 'export BRANCH_NAME="$CIRCLE_BRANCH"' >> "$BASH_ENV" - echo 'export TAG_NAME="$CIRCLE_TAG"' >> "$BASH_ENV" - fi - - # Exporting / sourcing the path before installing the tool prevents the message about the PATH - echo 'export PATH="$PATH:/root/.dotnet/tools"' >> "$BASH_ENV" - - build_test: - description: "Builds and tests" - parameters: - debug-build: - type: boolean - default: false - steps: - - checkout - - set_env - - run: - name: init - command: | - chmod ugo+x ./*.sh - chmod ugo+x ./tests/scripts/*.sh - - # Use v1.4.1 as this supports .NET Core 2.1. Version 1.5.0 is for .NET Core 2.2. - dotnet tool install -g --version=1.4.1 coverlet.console - - run: - # build has to be from the build.sh in order to pick the correct version numbers, etc. - name: build - command: ./build.sh build - - run: - name: test - command: ./build.sh test - - unless: - condition: << parameters.debug-build >> - steps: - - run: - name: pack - command: ./build.sh pack - - run: - name: global tool install (trx2junit) - command: | - # copied from build.sh (but modified) - if [[ -n "$CIRCLE_TAG" ]]; then - if [[ "$CIRCLE_TAG" =~ ^v([0-9]+)\.([0-9]+)\.([0-9]+)(-(preview-[0-9]+))$ ]]; then - VersionMajor="${BASH_REMATCH[1]}" - VersionMinor="${BASH_REMATCH[2]}" - VersionPatch="${BASH_REMATCH[3]}" - VersionSuffix="${BASH_REMATCH[5]}" - - ToolVersion="$VersionMajor.$VersionMinor.$VersionPatch-$VersionSuffix" - fi - fi - - # special handling for pre-releases (is a constraint by .net Core global tools) - # and also to prevent installation from NuGet-feed (which may have higher version than the - # built tool) - if [[ -z "$ToolVersion" ]]; then - dotnet tool install -g --configfile=ci-nuget.config trx2junit - else - dotnet tool install -g --version="$ToolVersion" --configfile=ci-nuget.config trx2junit - fi - - run: - name: global tool install (trx2junit) - when: on_fail - command: | - echo "Previous job failed, so use trx2junit from NuGet instead of local copy" - echo "" - dotnet tool install -g trx2junit - - run: - name: project test results - when: always - command: | - if [[ -d "tests/TestResults" ]]; then - trx2junit tests/TestResults/*.trx - #rm tests/TestResults/*.trx - fi - - run: - name: code coverage - command: | - if [[ -z "$CODECOV_TOKEN" ]]; then - echo "codecov token not set -- skipping code coverage" - else - ./build.sh coverage - fi - - run: ./tests/scripts/run-samples.sh - - run: ./tests/scripts/run-single-arg.sh - - run: ./tests/scripts/run-multiple-args.sh - - run: ./tests/scripts/run-globbing.sh - - run: ./tests/scripts/run-no-globbing.sh - - run: ./tests/scripts/run-different-output-location.sh - - run: ./tests/scripts/run-junit2trx.sh - - persist_to_workspace: - root: . - paths: - - build.sh - - NuGet-Packed - - store_test_results: - path: tests/TestResults - - store_artifacts: - path: tests/TestResults - destination: TestResults - -jobs: - release_build_test_job: - executor: build-executor - steps: - - build_test - - debug_build_test_job: - executor: build-executor - environment: - BUILD_CONFIG: Debug - steps: - - build_test: - debug-build: true - - deploy_job: - executor: deploy-executor - parameters: - target: - type: string - steps: - - set_env - - attach_workspace: - at: . - - run: - name: deploy to << parameters.target >> - command: | - chmod ugo+x ./build.sh - ./build.sh deploy << parameters.target >> - -workflows: - version: 2 - build_test_deploy: - jobs: - - release_build_test_job: - filters: - tags: - only: /^v[0-9]+\.[0-9]+\.[0-9]+(-preview-[0-9]+)?$/ - - - debug_build_test_job: - filters: - tags: - only: /^v[0-9]+\.[0-9]+\.[0-9]+(-preview-[0-9]+)?$/ - - - deploy_job: - name: deploy-nuget - target: nuget # parameter - requires: - - release_build_test_job - - debug_build_test_job - filters: - branches: - ignore: /.*/ - tags: - only: /^v[0-9]+\.[0-9]+\.[0-9]+$/ - context: org-global - - - deploy_job: - name: deploy-myget - target: myget # parameter - requires: - - release_build_test_job - - debug_build_test_job - filters: - branches: - #only: master - ignore: /.*/ - tags: - only: /^v[0-9]+\.[0-9]+\.[0-9]+(-preview-[0-9]+)?$/ - context: org-global diff --git a/.editorconfig b/.editorconfig index 8b9b676..305ac90 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,14 +4,17 @@ root = true # all files [*] +charset = utf-8 + # Indentation and spacing -indent_size = 4 indent_style = space +indent_size = 4 tab_width = 4 # New line preferences end_of_line = lf insert_final_newline = true +trim_trailing_whitespace = true # C# files [*.cs] @@ -179,11 +182,19 @@ dotnet_naming_rule.private_or_internal_static_field_should_be_static_field.sever dotnet_naming_rule.private_or_internal_static_field_should_be_static_field.symbols = private_or_internal_static_field dotnet_naming_rule.private_or_internal_static_field_should_be_static_field.style = static_field +dotnet_naming_rule.private_or_internal_field_should_be_instance_field.severity = suggestion +dotnet_naming_rule.private_or_internal_field_should_be_instance_field.symbols = private_or_internal_field +dotnet_naming_rule.private_or_internal_field_should_be_instance_field.style = instance_field + # Symbol specifications dotnet_naming_symbols.interface.applicable_kinds = interface dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.interface.required_modifiers = +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.private_or_internal_field.applicable_kinds = field +dotnet_naming_symbols.private_or_internal_field.applicable_accessibilities = internal, private, private_protected +dotnet_naming_symbols.private_or_internal_field.required_modifiers = dotnet_naming_symbols.private_or_internal_static_field.applicable_kinds = field dotnet_naming_symbols.private_or_internal_static_field.applicable_accessibilities = internal, private, private_protected @@ -191,11 +202,11 @@ dotnet_naming_symbols.private_or_internal_static_field.required_modifiers = stat dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.types.required_modifiers = +dotnet_naming_symbols.types.required_modifiers = dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected -dotnet_naming_symbols.non_field_members.required_modifiers = +dotnet_naming_symbols.non_field_members.required_modifiers = dotnet_naming_symbols.constant.applicable_kinds = field dotnet_naming_symbols.constant.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected @@ -203,17 +214,22 @@ dotnet_naming_symbols.constant.required_modifiers = const # Naming styles -dotnet_naming_style.pascal_case.required_prefix = -dotnet_naming_style.pascal_case.required_suffix = -dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = dotnet_naming_style.pascal_case.capitalization = pascal_case dotnet_naming_style.begins_with_i.required_prefix = I -dotnet_naming_style.begins_with_i.required_suffix = -dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = dotnet_naming_style.begins_with_i.capitalization = pascal_case dotnet_naming_style.static_field.required_prefix = s_ -dotnet_naming_style.static_field.required_suffix = -dotnet_naming_style.static_field.word_separator = +dotnet_naming_style.static_field.required_suffix = +dotnet_naming_style.static_field.word_separator = dotnet_naming_style.static_field.capitalization = camel_case + +dotnet_naming_style.instance_field.required_prefix = _ +dotnet_naming_style.instance_field.required_suffix = +dotnet_naming_style.instance_field.word_separator = +dotnet_naming_style.instance_field.capitalization = camel_case diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..2a0c8a1 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @gfoidl diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml new file mode 100644 index 0000000..3774ed3 --- /dev/null +++ b/.github/workflows/automerge.yml @@ -0,0 +1,42 @@ +# Reminder: +# ========= +# branch protection rules must be set, otherwise the automerge will happen as soon as the label +# is applied and not when CI is readay. + +name: automerge +on: + pull_request: + types: + - labeled + - unlabeled + - synchronize + - opened + - edited + - ready_for_review + - reopened + - unlocked + + pull_request_review: + types: + - submitted + + check_suite: + types: + - completed + + status: {} + +jobs: + automerge: + runs-on: ubuntu-latest + steps: + - name: automerge + uses: "pascalgn/automerge-action@v0.8.0" + #with: + # args: "--trace" + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + MERGE_LABELS: "automerge" + #MERGE_REMOVE_LABELS: "automerge" # https://github.com/pascalgn/automerge-action/issues/56 + MERGE_METHOD: "squash" + MERGE_DELETE_BRANCH: "true" diff --git a/.gitignore b/.gitignore index d5e8c75..920f539 100644 --- a/.gitignore +++ b/.gitignore @@ -220,3 +220,5 @@ _Pvt_Extensions /source/packages /NuGet-Packed /tests/*.playlist +/tests/Coverage +/tests/trx2junit.Tests/coverage.*.cobertura.xml diff --git a/ReadMe.md b/ReadMe.md index b14bce9..c6c603f 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -1,6 +1,6 @@ -| CircleCI | Code Coverage | NuGet | -| -- | -- | -- | -| [![CircleCI](https://circleci.com/gh/gfoidl/trx2junit/tree/master.svg?style=svg)](https://circleci.com/gh/gfoidl/trx2junit/tree/master)| [![codecov](https://codecov.io/gh/gfoidl/trx2junit/branch/master/graph/badge.svg)](https://codecov.io/gh/gfoidl/trx2junit) | [![NuGet](https://img.shields.io/nuget/v/trx2junit.svg?style=flat-square)](https://www.nuget.org/packages/trx2junit/) | +| CI | NuGet | +| -- | -- | +| [![Build Status](https://dev.azure.com/gh-gfoidl/github-Projects/_apis/build/status/.NET/trx2junit?branchName=master)](https://dev.azure.com/gh-gfoidl/github-Projects/_build/latest?definitionId=35&branchName=master) | [![NuGet](https://img.shields.io/nuget/v/trx2junit.svg?style=flat-square)](https://www.nuget.org/packages/trx2junit/) | # trx2junit (.NET Core global tool) diff --git a/build.sh b/build.sh index 10b62cb..02613c5 100644 --- a/build.sh +++ b/build.sh @@ -1,13 +1,14 @@ -#!/bin/bash +#!/usr/bin/env bash # ## CI build script for projects based on gfoidl's schema. # # Arguments: # build builds the solution # test runs all tests under ./tests +# test-coverage test + determines code coverage with coverlet.msbuild # coverage determines code coverage with coverlet and uploads to codecov # pack creates the NuGet-package -# deploy deploys to $2, which must be either nuget or myget +# deploy deploys to $2, which must be either nuget or custom # * when CI_SKIP_DEPLOY is set, no deploy is done # * when DEBUG is set, the action is echoed and not done # @@ -18,24 +19,27 @@ # TAG_NAME tag the commit is on # CI_SKIP_DEPLOY when set no deploy is done, even if deploy is called # DEBUG when set deploy is simulted by echoing the action +# TEST_DIR directory in which to search for test-assemblies # TEST_FRAMEWORK when set only the specified test-framework (dotnet test -f) will be used # TESTS_TO_SKIP a list of test-projects to skip / ignore, separated by ; # CODECOV_TOKEN the token for codecov to uploads the opencover-xml +# MOVE_TRX moves the test-results (trx) to tests/TestResults # # Functions (sorted alphabetically): # build builds the solution # coverage code coverage -# deploy deploys the solution either to nuget or myget +# deploy deploys the solution either to nuget or custom # main entry-point # pack creates the NuGet-package # setBuildEnv sets the environment variables regarding the build-environment # test runs tests for projects in ./tests +# test-coverage runs tests for projects in ./tests and collects code-coverage # _coverageCore helper -- used by coverage # _deployCore helper -- used by deploy # _testCore helper -- used by test # # Exit-codes: -# 101 deploy target is neither 'nuget' nor 'myget', so it is unknown +# 101 deploy target is neither 'nuget' nor 'custom', so it is unknown # 102 no args given for script, help is displayed and exited # $? exit-code for build-step is returned unmodified #------------------------------------------------------------------------------ @@ -46,13 +50,14 @@ help() { echo "Arguments:" echo " build builds the solution" echo " test runs all tests under ./tests" + echo " test-coverage test + determines code coverage with coverlet.msbuild" echo " coverage determines code coverage with coverlet and uploads to codecov" echo " pack creates the NuGet-package" - echo " deploy [nuget|myget] deploys to the destination" + echo " deploy [nuget|custom] deploys to the destination" } #------------------------------------------------------------------------------ setBuildEnv() { - export BUILD_CONFIG=${BUILD_CONFIG-Release} + export BUILD_CONFIG=${BUILD_CONFIG:-Release} # BuildNumber is used by MsBuild for version information. # ci tools clone usually to depth 50, so this is not good @@ -78,7 +83,7 @@ setBuildEnv() { #------------------------------------------------------------------------------ build() { dotnet restore - dotnet build -c $BUILD_CONFIG --no-restore + dotnet build -c "$BUILD_CONFIG" --no-restore } #------------------------------------------------------------------------------ _testCore() { @@ -89,7 +94,7 @@ _testCore() { local testDir local testNameWOExtension local testName - local testResultName + #local testResultName local dotnetTestArgs local testsToSkip @@ -97,8 +102,10 @@ _testCore() { testDir=$(dirname "$testFullName") testNameWOExtension=$(basename "$testDir") testName=$(basename "$testFullName") - testResultName="$testNameWOExtension-$(date +%s)" - dotnetTestArgs="-c $BUILD_CONFIG --no-build --verbosity quiet --logger \"trx;LogFileName=$testResultName.trx\" $testFullName" + #testResultName="$testNameWOExtension-$(date +%Y%m%d-%H%M%S_%N)" + + #dotnetTestArgs=("-c ${BUILD_CONFIG}" "--no-build" "--verbosity normal" "--logger \"trx;LogFileName=${testResultName}.trx\"") + dotnetTestArgs=("-c ${BUILD_CONFIG}" "--no-build" "--verbosity normal" "--logger \"trx\"") if [[ -n "$TESTS_TO_SKIP" ]]; then testsToSkip=(${TESTS_TO_SKIP//;/ }) @@ -112,22 +119,38 @@ _testCore() { fi echo "" - echo "test framework: ${TEST_FRAMEWORK-not specified}" + echo "test framework: ${TEST_FRAMEWORK:-not specified}" echo "test fullname: $testFullName" echo "testing: $testName..." - echo "test result name: $testResultName" + #echo "test result name: $testResultName" echo "" if [[ -n "$TEST_FRAMEWORK" ]]; then - dotnetTestArgs="-f $TEST_FRAMEWORK $dotnetTestArgs" + dotnetTestArgs+=("-f ${TEST_FRAMEWORK}") fi - dotnet test $dotnetTestArgs + if [[ -n "$collectCoverage" ]]; then + echo "running tests and collecting code coverage" + echo "" + + # Strange but git-bash (Windows) needs double-escapes, so //p:CollectCoverage=true + # whereas non-Windows just needs /p:CollectCoverage=true + # To avoid platform detection like [[ $(uname | grep mingw -i | wc -l) -ge 0 ]] + # use -p instead to be on the safe side. + dotnetTestArgs+=("-p:CollectCoverage=true" "-p:CoverletOutputFormat=cobertura") + fi + + dotnetTestArgs+=("${testFullName}") + dotnet test ${dotnetTestArgs[@]} local result=$? - mkdir -p "./tests/TestResults" - mv $testDir/TestResults/$testResultName*.trx ./tests/TestResults + if [[ -n "$MOVE_TRX" ]]; then + mkdir -p "./tests/TestResults" + mv "$testDir"/TestResults/*.trx ./tests/TestResults + + echo "moved test-results (trx) to tests/TestResults" + fi if [[ $result != 0 ]]; then exit $result @@ -139,7 +162,7 @@ _testCore() { #------------------------------------------------------------------------------ test() { local testDir - testDir="./tests" + testDir="${TEST_DIR:-./tests}" if [[ ! -d "$testDir" ]]; then echo "test-directory not existing -> no test need to run" @@ -152,6 +175,27 @@ test() { done } #------------------------------------------------------------------------------ +test_coverage() { + collectCoverage=1 + test + + # Each test-project has it's coverage report, so merge them to one report + + echo "check if dotnet-reportgenerator-globaltool is installed..." + + if [[ $(dotnet tool list -g | grep dotnet-reportgenerator-globaltool | wc -l) -eq 0 ]]; then + echo "not installed -> will install it" + export PATH="$PATH:$HOME/.dotnet/tools" + dotnet tool install -g dotnet-reportgenerator-globaltool + else + echo "already installed" + fi + + echo "" + + reportgenerator -reports:tests/**/*.cobertura.xml -targetdir:tests/Coverage -reporttypes:"Cobertura;HtmlInline_AzurePipelines" +} +#------------------------------------------------------------------------------ _coverageCore() { local testFullName local testDir @@ -180,6 +224,15 @@ coverage() { return fi + echo "check if coverlet.console is installed..." + if [[ $(dotnet tool list -g | grep coverlet.console | wc -l) -eq 0 ]]; then + echo "not installed -> will install it" + export PATH="$PATH:$HOME/.dotnet/tools" + dotnet tool install -g coverlet.console + else + echo "already installed" + fi + for testProject in "$testDir"/**/*.csproj; do _coverageCore "$testProject" done @@ -193,7 +246,7 @@ coverage() { fi # a cool script, does quite a lot without any args :-) - ./codecov.sh + ./codecov.sh -Z else echo "CODECOV_TOKEN not set -- skipping upload" fi @@ -223,8 +276,8 @@ deploy() { if [[ "$1" == "nuget" ]]; then _deployCore "$NUGET_FEED" "$NUGET_KEY" - elif [[ "$1" == "myget" ]]; then - _deployCore "$MYGET_FEED" "$MYGET_KEY" + elif [[ "$1" == "custom" ]]; then + _deployCore "$CUSTOM_FEED" "$CUSTOM_KEY" elif [[ "$1" == "local" ]]; then echo "Skipping deploy because 'local'" else @@ -237,22 +290,29 @@ main() { setBuildEnv case "$1" in - build) build - ;; - test) test - ;; - coverage) coverage - ;; - pack) pack - ;; + build) + build + ;; + test) + test + ;; + test-coverage) + test_coverage + ;; + coverage) + coverage + ;; + pack) + pack + ;; deploy) - shift - deploy "$1" - ;; + shift + deploy "$1" + ;; *) - help - exit - ;; + help + exit + ;; esac } #------------------------------------------------------------------------------ diff --git a/source/trx2junit/Program.cs b/source/trx2junit/Program.cs index 344b802..1a3e8a3 100644 --- a/source/trx2junit/Program.cs +++ b/source/trx2junit/Program.cs @@ -1,9 +1,11 @@ -using System; +using System; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; namespace trx2junit { + [ExcludeFromCodeCoverage] static class Program { static async Task Main(string[] args) diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props index d1dfb59..8e10d5b 100644 --- a/tests/Directory.Build.props +++ b/tests/Directory.Build.props @@ -1,4 +1,4 @@ - + @@ -8,7 +8,7 @@ - + diff --git a/tests/trx2junit.Tests/trx2junit.Tests.csproj b/tests/trx2junit.Tests/trx2junit.Tests.csproj index c165c44..27cb175 100644 --- a/tests/trx2junit.Tests/trx2junit.Tests.csproj +++ b/tests/trx2junit.Tests/trx2junit.Tests.csproj @@ -1,4 +1,4 @@ - + $(TestTfm) @@ -9,9 +9,16 @@ - - - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/trx2junit.sln b/trx2junit.sln index 589780d..a2b935e 100644 --- a/trx2junit.sln +++ b/trx2junit.sln @@ -25,11 +25,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{292C2B9E verify-xml.sh = verify-xml.sh EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".circleci", ".circleci", "{148DD984-1D1C-4A97-A5F5-907812DCA662}" - ProjectSection(SolutionItems) = preProject - .circleci\config.yml = .circleci\config.yml - EndProjectSection -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{15D1FCD7-8027-4DD0-BF1A-BFC295C99332}" ProjectSection(SolutionItems) = preProject samples\Directory.Build.props = samples\Directory.Build.props @@ -66,6 +61,38 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{3531 tests\scripts\run-single-arg.sh = tests\scripts\run-single-arg.sh EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{BA2AD413-4251-4C08-9BE5-402DCA7CAA41}" + ProjectSection(SolutionItems) = preProject + .github\CODEOWNERS = .github\CODEOWNERS + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ISSUE_TEMPLATE", "ISSUE_TEMPLATE", "{9F873D32-DF04-4453-9604-716F5598AC7E}" + ProjectSection(SolutionItems) = preProject + .github\ISSUE_TEMPLATE\bug_report.md = .github\ISSUE_TEMPLATE\bug_report.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{8819C279-C1A2-420B-A1FD-28F4097EC41C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{A21A387B-90BB-45B0-AD48-240B86DA1FE9}" + ProjectSection(SolutionItems) = preProject + .github\workflows\automerge.yml = .github\workflows\automerge.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".azure", ".azure", "{5101A9AB-5CCE-4A20-AEA5-432BD2E4D9D2}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "pipelines", "pipelines", "{8A80B7B0-F9B3-45C9-8796-C8049C43CD7E}" + ProjectSection(SolutionItems) = preProject + .azure\pipelines\ci.yml = .azure\pipelines\ci.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "jobs", "jobs", "{BF2D34D4-5549-4098-994B-FACDBD730C56}" + ProjectSection(SolutionItems) = preProject + .azure\pipelines\jobs\build_and_test.yml = .azure\pipelines\jobs\build_and_test.yml + .azure\pipelines\jobs\coverage.yml = .azure\pipelines\jobs\coverage.yml + .azure\pipelines\jobs\deploy_nuget.yml = .azure\pipelines\jobs\deploy_nuget.yml + .azure\pipelines\jobs\e2e_tests.yml = .azure\pipelines\jobs\e2e_tests.yml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -98,12 +125,18 @@ Global EndGlobalSection GlobalSection(NestedProjects) = preSolution {FD8336E3-655B-430A-A72F-377E83D54C63} = {49823B21-7C47-43DF-B3C1-4F48E2237EAD} - {148DD984-1D1C-4A97-A5F5-907812DCA662} = {292C2B9E-E093-4476-AE17-A08F144AC27A} {AB4EB02B-E16A-40B7-8524-A4F752FDFBBA} = {15D1FCD7-8027-4DD0-BF1A-BFC295C99332} {BA191F72-A499-4D5B-972C-66EF3BD24B99} = {15D1FCD7-8027-4DD0-BF1A-BFC295C99332} {27B8F9C6-B0D5-4E66-8AB7-12A23D4EEFB9} = {B42081F2-652A-46C6-8E10-D727A3C360A1} {CE59E4DA-EB17-49A6-A6E8-1484A3413C46} = {15D1FCD7-8027-4DD0-BF1A-BFC295C99332} {3531998A-0501-4259-A130-D982412978AC} = {B42081F2-652A-46C6-8E10-D727A3C360A1} + {BA2AD413-4251-4C08-9BE5-402DCA7CAA41} = {B5D05395-4D8B-40A2-A03E-BFF4E76D7F7B} + {9F873D32-DF04-4453-9604-716F5598AC7E} = {BA2AD413-4251-4C08-9BE5-402DCA7CAA41} + {8819C279-C1A2-420B-A1FD-28F4097EC41C} = {292C2B9E-E093-4476-AE17-A08F144AC27A} + {A21A387B-90BB-45B0-AD48-240B86DA1FE9} = {8819C279-C1A2-420B-A1FD-28F4097EC41C} + {5101A9AB-5CCE-4A20-AEA5-432BD2E4D9D2} = {292C2B9E-E093-4476-AE17-A08F144AC27A} + {8A80B7B0-F9B3-45C9-8796-C8049C43CD7E} = {5101A9AB-5CCE-4A20-AEA5-432BD2E4D9D2} + {BF2D34D4-5549-4098-994B-FACDBD730C56} = {8A80B7B0-F9B3-45C9-8796-C8049C43CD7E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4A779269-CD77-4716-9E1A-DF4AE5817A41}