Compare commits

...

31 Commits

Author SHA1 Message Date
irongut 5088d5eb31 prepare v1.2.0 release 2021-11-25 21:53:22 +00:00
irongut ee53bdd6c5 updated workflows 2021-11-25 20:44:07 +00:00
irongut 6a542ff4b0 updated test workflows 2021-11-25 20:29:07 +00:00
irongut 46bc9869d5 updated test workflow 2021-11-24 23:33:15 +00:00
irongut 97f4a52b81 fixed release build workflow 2021-11-24 22:01:43 +00:00
irongut 95ea154a7b prepare v1.2.0-beta release 2021-11-24 21:47:59 +00:00
irongut 2e00d30f70 merge PR #30 Update to .Net 6 2021-11-24 20:27:20 +00:00
irongut c4d4b9a087 updated build workflows to .Net 6 #23 2021-11-24 00:34:44 +00:00
irongut ea5d3b417b updated build workflows to .Net 6 #23 2021-11-24 00:32:25 +00:00
irongut 3d33fafb37 updated Docker launch settings 2021-11-24 00:24:33 +00:00
irongut 3a41f0a5ea reduced number of Docker image layers 2021-11-23 21:54:11 +00:00
irongut dd5c8d3b75 updated Dockerfile to .Net 6 #23 2021-11-23 20:56:58 +00:00
irongut 4986e930de updated CLI to .Net 6 #23 2021-11-23 01:08:46 +00:00
irongut 37dca42320 added Assign to Project workflow 2021-11-22 02:19:37 +00:00
irongut 5ec10c1882 updated assign-pr-to-author action 2021-11-22 02:10:34 +00:00
irongut 56c9f3d623 merge PR #29 add PR Labeler action
PR: add PR Labeler action
2021-11-22 02:05:16 +00:00
irongut f36c88a82b configure pr labeler 2021-11-22 02:00:18 +00:00
irongut ffe316d009 added pr labeler action 2021-11-22 01:33:05 +00:00
irongut 1ea9b55e4d merge PR #27 from dependabot/Microsoft.VisualStudio.Azure.Containers.Tools.Targets-1.14.0
Bump Microsoft.VisualStudio.Azure.Containers.Tools.Targets from 1.11.1 to 1.14.0
2021-11-16 10:32:40 +00:00
dependabot[bot] fcb924f622 Bump Microsoft.VisualStudio.Azure.Containers.Tools.Targets
Bumps Microsoft.VisualStudio.Azure.Containers.Tools.Targets from 1.11.1 to 1.14.0.

---
updated-dependencies:
- dependency-name: Microsoft.VisualStudio.Azure.Containers.Tools.Targets
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-15 23:08:36 +00:00
irongut ed588922f2 merge PR #26 Support multiple cobertura files
PR: Support multiple cobertura files
2021-11-15 01:21:55 +00:00
irongut 6e12bd152f removed commented line 2021-11-15 00:33:15 +00:00
irongut 4ae964bab0 update ci build test command line #19 2021-11-15 00:24:46 +00:00
irongut 60646036b5 support multiple cobertura files in action definition #19 2021-11-15 00:18:30 +00:00
irongut 1c2edd9230 support multiple cobertura files in CLI #19 2021-11-14 23:51:30 +00:00
irongut 9caa66feee merge PR #25 Allow hiding Branch Rate + Complexity values in output
PR: Allow hiding Branch Rate + Complexity values in output
2021-11-14 00:41:09 +00:00
irongut 6c68cb69dd added hide branch rate + complexity to action definition #22 2021-11-13 23:53:03 +00:00
irongut 914b6fe5f9 fixed summary complexity should be bold in md output 2021-11-13 22:35:52 +00:00
irongut 46007a7270 hide branch rate + complexity for markdown output #22 2021-11-13 22:29:15 +00:00
irongut 71ae720dab hide branch rate + complexity for text output #22 2021-11-13 21:55:29 +00:00
irongut dd7d40d268 added hide branch + complixity CLI options #22 2021-11-13 21:29:36 +00:00
17 changed files with 269 additions and 101 deletions
+31
View File
@@ -0,0 +1,31 @@
# Configuration for PR Labeller Action
# See: https://github.com/actions/labeler/blob/master/README.md
# Examples
# Add 'label1' to PR if anything changes within 'example' folder or any subfolders
# label1:
# - example/**/*
# Add 'label2' to PR if any file changes within 'example2' folder
# label2: example2/*
Action:
- action.yml
Docker:
- Dockerfile
- .dockerignore
Options:
- src/CodeCoverageSummary/CommandLineOptions.cs
Parsing:
- src/CodeCoverageSummary/CodeSummary.cs
- src/CodeCoverageSummary/Program.cs
Summary:
- src/CodeCoverageSummary/CodeSummary.cs
- src/CodeCoverageSummary/Program.cs
DevOps:
- .github/**/*
+42
View File
@@ -0,0 +1,42 @@
name: Assign to Project
on:
issues:
types: [opened, labeled]
pull_request:
types: [opened, labeled]
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
jobs:
assign-to-project:
runs-on: ubuntu-latest
name: Assign to Project
steps:
- name: Assign Issues to Bugs
uses: srggrs/assign-one-project-github-action@1.3.1
if: contains(github.event.issue.labels.*.name, 'bug')
with:
project: 'https://github.com/irongut/CodeCoverageSummary/projects/1'
column_name: 'Needs triage'
- name: Assign Issues to Enhancements
uses: srggrs/assign-one-project-github-action@1.3.1
if: contains(github.event.issue.labels.*.name, 'enhancement')
with:
project: 'https://github.com/irongut/CodeCoverageSummary/projects/2'
column_name: 'To do'
- name: Assign PRs to Bugs
uses: srggrs/assign-one-project-github-action@1.3.1
if: contains(github.event.pull_request.labels.*.name, 'bug')
with:
project: 'https://github.com/irongut/CodeCoverageSummary/projects/1'
column_name: 'In Progress'
- name: Assign PRs to Enhancements
uses: srggrs/assign-one-project-github-action@1.3.1
if: contains(github.event.pull_request.labels.*.name, 'enhancement')
with:
project: 'https://github.com/irongut/CodeCoverageSummary/projects/2'
column_name: 'In Progress'
+5 -3
View File
@@ -2,14 +2,16 @@
# https://github.com/samspills/assign-pr-to-author # https://github.com/samspills/assign-pr-to-author
name: Auto Assign PR name: Auto Assign PR
on: [pull_request]
on:
pull_request:
types: [opened]
jobs: jobs:
assignAuthor: assignAuthor:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Auto Assign PR - name: Auto Assign PR
uses: samspills/assign-pr-to-author@v1.0 uses: samspills/assign-pr-to-author@v1.0.1
if: github.event_name == 'pull_request' && github.event.action == 'opened'
with: with:
repo-token: '${{ secrets.GITHUB_TOKEN }}' repo-token: '${{ secrets.GITHUB_TOKEN }}'
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
- name: Setup .Net - name: Setup .Net
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v1
with: with:
dotnet-version: 5.0.x dotnet-version: 6.0.x
- name: Restore Dependencies - name: Restore Dependencies
run: dotnet restore src/CodeCoverageSummary.sln run: dotnet restore src/CodeCoverageSummary.sln
@@ -26,4 +26,4 @@ jobs:
run: dotnet build src/CodeCoverageSummary.sln --configuration Release --no-restore run: dotnet build src/CodeCoverageSummary.sln --configuration Release --no-restore
- name: Test with sample file - name: Test with sample file
run: dotnet src/CodeCoverageSummary/bin/Release/net5.0/CodeCoverageSummary.dll src/coverage.cobertura.xml --badge true run: dotnet src/CodeCoverageSummary/bin/Release/net6.0/CodeCoverageSummary.dll --files src/coverage.cobertura.xml --badge true
+16
View File
@@ -0,0 +1,16 @@
# Applies labels to pull requests based on paths & files modified in the pull request.
#
# .github/labeler.yml contains the list of labels & files / folders to match, see:
# https://github.com/actions/labeler/blob/master/README.md
name: PR Labeler
on:
pull_request_target:
jobs:
label:
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v3
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
+2 -24
View File
@@ -21,7 +21,7 @@ jobs:
- name: Setup .Net - name: Setup .Net
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v1
with: with:
dotnet-version: 5.0.x dotnet-version: 6.0.x
- name: Restore Dependencies - name: Restore Dependencies
run: dotnet restore src/CodeCoverageSummary.sln run: dotnet restore src/CodeCoverageSummary.sln
@@ -30,29 +30,7 @@ jobs:
run: dotnet build src/CodeCoverageSummary.sln --configuration Release --no-restore run: dotnet build src/CodeCoverageSummary.sln --configuration Release --no-restore
- name: Test with sample file - name: Test with sample file
run: dotnet src/CodeCoverageSummary/bin/Release/net5.0/CodeCoverageSummary.dll src/coverage.cobertura.xml --badge true run: dotnet src/CodeCoverageSummary/bin/Release/net6.0/CodeCoverageSummary.dll --files src/coverage.cobertura.xml --badge true
- name: Get Previous Tag
id: get_previous_tag
run: |
PREV_TAG=$(git describe --abbrev=0 --tags "${{ github.ref }}^")
echo "::set-output name=baseRef::${PREV_TAG}"
- name: Generate Changelog
id: generate_changelog
uses: nblagoev/pull-release-notes-action@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
base-ref: ${{ steps.get_previous_tag.outputs.baseRef }}
head-ref: ${{ github.ref }}
- name: Add Changelog to Release
uses: irongut/EditRelease@v1.0.0
with:
token: ${{ secrets.GITHUB_TOKEN }}
id: ${{ github.event.release.id }}
body: ${{ steps.generate_changelog.outputs.result }}
deploy: deploy:
name: Deploy to GHCR name: Deploy to GHCR
+6 -2
View File
@@ -13,8 +13,12 @@ jobs:
- name: Test Action - name: Test Action
uses: irongut/CodeCoverageSummary@master uses: irongut/CodeCoverageSummary@master
with: with:
filename: '/app/sample.coverage.xml' filename: /app/sample.coverage.xml,/app/sample.coverage.xml
badge: true badge: true
format: 'md'
fail_below_min: true fail_below_min: true
format: markdown
hide_branch_rate: false
hide_complexity: true
indicators: true
output: both
thresholds: '60 80' thresholds: '60 80'
+9 -2
View File
@@ -14,5 +14,12 @@ jobs:
- name: Test Action - name: Test Action
uses: irongut/CodeCoverageSummary@master uses: irongut/CodeCoverageSummary@master
with: with:
filename: '/app/sample.coverage.xml' filename: /app/sample.coverage.xml,/app/sample.coverage.xml
badge: 'true' badge: true
fail_below_min: true
format: markdown
hide_branch_rate: false
hide_complexity: true
indicators: true
output: both
thresholds: '60 80'
+9 -2
View File
@@ -14,5 +14,12 @@ jobs:
- name: Test Action - name: Test Action
uses: irongut/CodeCoverageSummary@master uses: irongut/CodeCoverageSummary@master
with: with:
filename: '/app/sample.coverage.xml' filename: /app/sample.coverage.xml,/app/sample.coverage.xml
badge: 'true' badge: true
fail_below_min: true
format: markdown
hide_branch_rate: false
hide_complexity: true
indicators: true
output: both
thresholds: '60 80'
+6 -7
View File
@@ -1,11 +1,10 @@
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
COPY ["src/coverage.cobertura.xml", "/publish/sample.coverage.xml"]
WORKDIR /src WORKDIR /src
COPY ["src/CodeCoverageSummary/CodeCoverageSummary.csproj", "CodeCoverageSummary/"] COPY ["src/CodeCoverageSummary/CodeCoverageSummary.csproj", "CodeCoverageSummary/"]
RUN dotnet restore CodeCoverageSummary/CodeCoverageSummary.csproj RUN dotnet restore CodeCoverageSummary/CodeCoverageSummary.csproj
COPY ["src/CodeCoverageSummary", "CodeCoverageSummary/"] COPY ["src/CodeCoverageSummary", "CodeCoverageSummary/"]
COPY ["src/coverage.cobertura.xml", "sample.coverage.xml"] RUN dotnet publish CodeCoverageSummary/CodeCoverageSummary.csproj --configuration Release --no-restore --output /publish
RUN dotnet build CodeCoverageSummary/CodeCoverageSummary.csproj --configuration Release --no-restore --output /app/build
RUN dotnet publish CodeCoverageSummary/CodeCoverageSummary.csproj --configuration Release --no-restore --output /app/publish
# Label the container # Label the container
LABEL maintainer="Irongut <murray.dave@outlook.com>" LABEL maintainer="Irongut <murray.dave@outlook.com>"
@@ -18,8 +17,8 @@ LABEL com.github.actions.description="A GitHub Action that reads Cobertura forma
LABEL com.github.actions.icon="book-open" LABEL com.github.actions.icon="book-open"
LABEL com.github.actions.color="purple" LABEL com.github.actions.color="purple"
FROM mcr.microsoft.com/dotnet/runtime:5.0 AS final FROM mcr.microsoft.com/dotnet/runtime:6.0 AS final
WORKDIR /app WORKDIR /app
COPY --from=build /app/publish . COPY --from=build /publish .
COPY --from=build /src/sample.coverage.xml . ENV DOTNET_EnableDiagnostics=0
ENTRYPOINT ["dotnet", "/app/CodeCoverageSummary.dll"] ENTRYPOINT ["dotnet", "/app/CodeCoverageSummary.dll"]
+64 -15
View File
@@ -1,19 +1,33 @@
# Code Coverage Summary # Code Coverage Summary
<div align="center">
[![CI Build](https://github.com/irongut/CodeCoverageSummary/actions/workflows/ci-build.yml/badge.svg)](https://github.com/irongut/CodeCoverageSummary/actions/workflows/ci-build.yml)
&nbsp;
[![GitHub](https://img.shields.io/badge/GitHub-irongut/CodeCoverageSummary-informational?style=flat&logo=github)](https://github.com/irongut/CodeCoverageSummary)
&nbsp;
![.NET 6.0](https://img.shields.io/badge/Version-.NET%206.0-informational?style=flat&logo=dotnet)
&nbsp;
![Built With Docker](https://img.shields.io/badge/Built_With-Docker-informational?style=flat&logo=docker)
</div>
A GitHub Action that reads Cobertura format code coverage files from your test suite and outputs a text or markdown summary. This summary can be posted as a Pull Request comment or included in Release Notes by other actions to give you an immediate insight into the health of your code without using a third-party site. A GitHub Action that reads Cobertura format code coverage files from your test suite and outputs a text or markdown summary. This summary can be posted as a Pull Request comment or included in Release Notes by other actions to give you an immediate insight into the health of your code without using a third-party site.
Code Coverage Summary is designed for use with [Coverlet](https://github.com/coverlet-coverage/coverlet) and [gcovr](https://github.com/gcovr/gcovr) but it should work with any test framework that outputs coverage in Cobertura format. Code Coverage Summary is designed for use with [Coverlet](https://github.com/coverlet-coverage/coverlet) and [gcovr](https://github.com/gcovr/gcovr) but it should work with any test framework that outputs coverage in Cobertura format. If it doesn't work with your tooling please [open an issue][new-issue] to discuss the problem.
As a Docker based action Code Coverage Summary requires a Linux runner, see [Types of Action](https://docs.github.com/en/actions/creating-actions/about-custom-actions#types-of-actions). If you need to build with a Windows or MacOS runner a workaround would be to upload the coverage file as an artifact and use a separate job with a Linux runner to generate the summary. As a Docker based action Code Coverage Summary requires a Linux runner, see [Types of Action](https://docs.github.com/en/actions/creating-actions/about-custom-actions#types-of-actions). If you need to build with a Windows or MacOS runner a workaround would be to upload the coverage file as an artifact and use a separate job with a Linux runner to generate the summary.
## Inputs ## Inputs
### `filename` ### `filename`
**Required** **Required**
Code coverage file to analyse. A comma separated list of code coverage files to analyse. If there are any spaces in a path or filename this value must be in quotes.
Note: Coverlet creates the coverage file in a random named directory (guid) so you need to copy it to a predictable path before running this Action, see the [.Net Workflow Example](#net-workflow-example) below.
Note: Coverlet creates the coverage file in a random named directory (guid) so you need to copy it to a predictable path before running this Action, see the [.Net 5 Workflow Example](#net-5-workflow-example) below.
### `badge` ### `badge`
@@ -27,14 +41,27 @@ equal or greater than upper threshold (75%) | ![Code Coverage](https://img.shiel
See [`thresholds`](#thresholds) to change these values. See [`thresholds`](#thresholds) to change these values.
### `fail_below_min` ### `fail_below_min`
Fail the workflow if the overall Line Rate is below lower threshold - `true` or `false` (default). The default lower threshold is 50%, see [`thresholds`](#thresholds). Fail the workflow if the overall Line Rate is below lower threshold - `true` or `false` (default). The default lower threshold is 50%, see [`thresholds`](#thresholds).
### `format` ### `format`
Output Format - `markdown` or `text` (default). Output Format - `markdown` or `text` (default).
### `hide_branch_rate`
Hide Branch Rate values in the output - `true` or `false` (default).
### `hide_complexity`
Hide Complexity values in the output - `true` or `false` (default).
### `indicators` ### `indicators`
Include health indicators in the output - `true` (default) or `false`. Include health indicators in the output - `true` (default) or `false`.
@@ -47,6 +74,7 @@ equal or greater than upper threshold (75%) | ✔
See [`thresholds`](#thresholds) to change these values. See [`thresholds`](#thresholds) to change these values.
### `output` ### `output`
Output Type - `console` (default), `file` or `both`. Output Type - `console` (default), `file` or `both`.
@@ -57,10 +85,12 @@ Output Type - `console` (default), `file` or `both`.
`both` will output the coverage summary to the Action log and a file as above. `both` will output the coverage summary to the Action log and a file as above.
### `thresholds` ### `thresholds`
Lower and upper threshold percentages for badge and health indicators, lower threshold can also be used to fail the action. Separate the values with a space and enclose them in quotes; default `'50 75'`. Lower and upper threshold percentages for badge and health indicators, lower threshold can also be used to fail the action. Separate the values with a space and enclose them in quotes; default `'50 75'`.
## Outputs ## Outputs
### Text Example ### Text Example
@@ -73,22 +103,34 @@ Summary: Line Rate = 83% (1212 / 1460), Branch Rate = 69% (262 / 378), Complexit
Minimum allowed line rate is 50% Minimum allowed line rate is 50%
``` ```
### Markdown Example ### Markdown Example
![image](https://user-images.githubusercontent.com/27953302/117726304-4ac1c100-b1de-11eb-8d9a-6286ba1f5523.png)
> ![Code Coverage](https://img.shields.io/badge/Code%20Coverage-83%25-success?style=flat)
>
> Package | Line Rate | Branch Rate | Complexity | Health
> -------- | --------- | ----------- | ---------- | ------
> Company.Example | 83% | 69% | 671 | ✔
> Company.Example.Library | 27% | 100% | 11 | ❌
> **Summary** | **83%** (1212 / 1460) | **69%** (262 / 378) | 682 | ✔
>
> _Minimum allowed line rate is `50%`_
## Usage ## Usage
```yaml ```yaml
name: Code Coverage Summary Report name: Code Coverage Summary Report
uses: irongut/CodeCoverageSummary@v1.1.0 uses: irongut/CodeCoverageSummary@v1.2.0
with: with:
filename: coverage/coverage.cobertura.xml filename: coverage.cobertura.xml
``` ```
### .Net 5 Workflow Example
### .Net Workflow Example
```yaml ```yaml
name: .Net 5 CI Build name: .Net 6 CI Build
on: on:
push: push:
@@ -107,7 +149,7 @@ jobs:
- name: Setup .NET - name: Setup .NET
uses: actions/setup-dotnet@v1 uses: actions/setup-dotnet@v1
with: with:
dotnet-version: 5.0.x dotnet-version: 6.0.x
- name: Restore Dependencies - name: Restore Dependencies
run: dotnet restore src/Example.sln run: dotnet restore src/Example.sln
@@ -119,17 +161,20 @@ jobs:
run: dotnet test src/Example.sln --configuration Release --no-build --verbosity normal --collect:"XPlat Code Coverage" --results-directory ./coverage run: dotnet test src/Example.sln --configuration Release --no-build --verbosity normal --collect:"XPlat Code Coverage" --results-directory ./coverage
- name: Copy Coverage To Predictable Location - name: Copy Coverage To Predictable Location
run: cp coverage/**/coverage.cobertura.xml coverage/coverage.cobertura.xml run: cp coverage/**/coverage.cobertura.xml coverage.cobertura.xml
- name: Code Coverage Summary Report - name: Code Coverage Summary Report
uses: irongut/CodeCoverageSummary@v1.1.0 uses: irongut/CodeCoverageSummary@v1.2.0
with: with:
filename: coverage/coverage.cobertura.xml filename: coverage.cobertura.xml
badge: true badge: true
fail_below_min: true fail_below_min: true
format: 'markdown' format: markdown
output: 'both' hide_branch_rate: false
thresholds: '70 80' hide_complexity: true
indicators: true
output: both
thresholds: '60 80'
- name: Add Coverage PR Comment - name: Add Coverage PR Comment
uses: marocchino/sticky-pull-request-comment@v2 uses: marocchino/sticky-pull-request-comment@v2
@@ -139,6 +184,7 @@ jobs:
path: code-coverage-results.md path: code-coverage-results.md
``` ```
## Contributing ## Contributing
### Report Bugs ### Report Bugs
@@ -147,10 +193,12 @@ Please make sure the bug is not already reported by searching existing [issues].
If you're unable to find an existing issue addressing the problem please [open a new one][new-issue]. Be sure to include a title and clear description, as much relevant information as possible, a workflow sample and any logs demonstrating the problem. If you're unable to find an existing issue addressing the problem please [open a new one][new-issue]. Be sure to include a title and clear description, as much relevant information as possible, a workflow sample and any logs demonstrating the problem.
### Suggest an Enhancement ### Suggest an Enhancement
Please [open a new issue][new-issue]. Please [open a new issue][new-issue].
### Submit a Pull Request ### Submit a Pull Request
Discuss your idea first, so that your changes have a good chance of being merged in. Discuss your idea first, so that your changes have a good chance of being merged in.
@@ -159,6 +207,7 @@ Submit your pull request against the `master` branch.
Pull requests that include documentation and relevant updates to README.md are merged faster, because you won't have to wait for somebody else to complete your contribution. Pull requests that include documentation and relevant updates to README.md are merged faster, because you won't have to wait for somebody else to complete your contribution.
## License ## License
Code Coverage Summary is available under the MIT license, see the [LICENSE](LICENSE) file for more info. Code Coverage Summary is available under the MIT license, see the [LICENSE](LICENSE) file for more info.
+15 -2
View File
@@ -6,7 +6,7 @@ branding:
color: purple color: purple
inputs: inputs:
filename: filename:
description: 'Code coverage file to analyse.' description: 'A comma separated list of code coverage files to analyse.'
required: true required: true
badge: badge:
description: 'Include a Line Rate coverage badge in the output using shields.io - true / false (default).' description: 'Include a Line Rate coverage badge in the output using shields.io - true / false (default).'
@@ -20,6 +20,14 @@ inputs:
description: 'Output Format - markdown or text (default).' description: 'Output Format - markdown or text (default).'
required: false required: false
default: 'text' default: 'text'
hide_branch_rate:
description: 'Hide Branch Rate values in the output - true / false (default).'
required: false
default: 'false'
hide_complexity:
description: 'Hide Complexity values in the output - true / false (default).'
required: false
default: 'false'
indicators: indicators:
description: 'Include health indicators in the output - true (default) / false.' description: 'Include health indicators in the output - true (default) / false.'
required: false required: false
@@ -34,8 +42,9 @@ inputs:
default: '50 75' default: '50 75'
runs: runs:
using: 'docker' using: 'docker'
image: 'docker://ghcr.io/irongut/codecoveragesummary:v1.1.0' image: 'docker://ghcr.io/irongut/codecoveragesummary:v1.2.0'
args: args:
- '--files'
- ${{ inputs.filename }} - ${{ inputs.filename }}
- '--badge' - '--badge'
- ${{ inputs.badge }} - ${{ inputs.badge }}
@@ -43,6 +52,10 @@ runs:
- ${{ inputs.fail_below_min }} - ${{ inputs.fail_below_min }}
- '--format' - '--format'
- ${{ inputs.format }} - ${{ inputs.format }}
- '--hidebranch'
- ${{ inputs.hide_branch_rate }}
- '--hidecomplexity'
- ${{ inputs.hide_complexity }}
- '--indicators' - '--indicators'
- ${{ inputs.indicators }} - ${{ inputs.indicators }}
- '--output' - '--output'
@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS> <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
<Company>Taranis Software</Company> <Company>Taranis Software</Company>
<Authors>Irongut</Authors> <Authors>Irongut</Authors>
@@ -14,12 +14,12 @@
<RepositoryUrl>https://github.com/irongut/CodeCoverageSummary</RepositoryUrl> <RepositoryUrl>https://github.com/irongut/CodeCoverageSummary</RepositoryUrl>
<RepositoryType>git</RepositoryType> <RepositoryType>git</RepositoryType>
<PackageTags>coverage test-coverage cobertura action code-coverage coverlet github-actions</PackageTags> <PackageTags>coverage test-coverage cobertura action code-coverage coverlet github-actions</PackageTags>
<Version>1.1.0</Version> <Version>1.2.0</Version>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.8.0" /> <PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.11.1" /> <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.14.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>
+1 -4
View File
@@ -31,9 +31,6 @@ namespace CodeCoverageSummary
public List<CodeCoverage> Packages { get; set; } public List<CodeCoverage> Packages { get; set; }
public CodeSummary() public CodeSummary() => Packages = new();
{
Packages = new List<CodeCoverage>();
}
} }
} }
+13 -2
View File
@@ -1,12 +1,13 @@
using CommandLine; using CommandLine;
using System; using System;
using System.Collections.Generic;
namespace CodeCoverageSummary namespace CodeCoverageSummary
{ {
public class CommandLineOptions public class CommandLineOptions
{ {
[Value(index: 0, Required = true, HelpText = "Code coverage file to analyse.")] [Option(longName: "files", Separator = ',', Required = true, HelpText = "A comma separated list of code coverage files to analyse.")]
public string Filename { get; set; } public IEnumerable<string> Files { get; set; }
[Option(longName: "badge", Required = false, HelpText = "Include a Line Rate coverage badge in the output using shields.io - true or false.", Default = "false")] [Option(longName: "badge", Required = false, HelpText = "Include a Line Rate coverage badge in the output using shields.io - true or false.", Default = "false")]
public string BadgeString { get; set; } public string BadgeString { get; set; }
@@ -21,6 +22,16 @@ namespace CodeCoverageSummary
[Option(longName: "format", Required = false, HelpText = "Output Format - markdown or text.", Default = "text")] [Option(longName: "format", Required = false, HelpText = "Output Format - markdown or text.", Default = "text")]
public string Format { get; set; } public string Format { get; set; }
[Option(longName: "hidebranch", Required = false, HelpText = "Hide Branch Rate values in the output - true or false.", Default = "false")]
public string HideBranchString { get; set; }
public bool HideBranchRate => HideBranchString.Equals("true", StringComparison.OrdinalIgnoreCase);
[Option(longName: "hidecomplexity", Required = false, HelpText = "Hide Complexity values in the output - true or false.", Default = "false")]
public string HideComplexityString { get; set; }
public bool HideComplexity => HideComplexityString.Equals("true", StringComparison.OrdinalIgnoreCase);
[Option(longName: "indicators", Required = false, HelpText = "Include health indicators in the output - true or false.", Default = "true")] [Option(longName: "indicators", Required = false, HelpText = "Include health indicators in the output - true or false.", Default = "true")]
public string IndicatorsString { get; set; } public string IndicatorsString { get; set; }
+43 -31
View File
@@ -19,18 +19,29 @@ namespace CodeCoverageSummary
{ {
try try
{ {
if (!File.Exists(o.Filename)) // check files exist
foreach (var file in o.Files)
{ {
Console.WriteLine("Error: Code coverage file not found."); if (!File.Exists(file))
return -2; // error {
Console.WriteLine($"Error: Code coverage file not found - {file}.");
return -2; // error
}
} }
// parse code coverage file // parse code coverage file
Console.WriteLine($"Code Coverage File: {o.Filename}"); CodeSummary summary = new();
CodeSummary summary = ParseTestResults(o.Filename); foreach (var file in o.Files)
if (summary == null)
{ {
Console.WriteLine("Error: Parsing code coverage file."); Console.WriteLine($"Code Coverage File: {file}");
summary = ParseTestResults(file, summary);
}
summary.LineRate /= o.Files.Count();
summary.BranchRate /= o.Files.Count();
if (summary.Packages.Count == 0)
{
Console.WriteLine("Error: Parsing code coverage file, no packages found.");
return -2; // error return -2; // error
} }
else else
@@ -48,14 +59,14 @@ namespace CodeCoverageSummary
if (o.Format.Equals("text", StringComparison.OrdinalIgnoreCase)) if (o.Format.Equals("text", StringComparison.OrdinalIgnoreCase))
{ {
fileExt = "txt"; fileExt = "txt";
output = GenerateTextOutput(summary, badgeUrl, o.Indicators); output = GenerateTextOutput(summary, badgeUrl, o.Indicators, o.HideBranchRate, o.HideComplexity);
if (o.FailBelowThreshold) if (o.FailBelowThreshold)
output += $"Minimum allowed line rate is {lowerThreshold * 100:N0}%{Environment.NewLine}"; output += $"Minimum allowed line rate is {lowerThreshold * 100:N0}%{Environment.NewLine}";
} }
else if (o.Format.Equals("md", StringComparison.OrdinalIgnoreCase) || o.Format.Equals("markdown", StringComparison.OrdinalIgnoreCase)) else if (o.Format.Equals("md", StringComparison.OrdinalIgnoreCase) || o.Format.Equals("markdown", StringComparison.OrdinalIgnoreCase))
{ {
fileExt = "md"; fileExt = "md";
output = GenerateMarkdownOutput(summary, badgeUrl, o.Indicators); output = GenerateMarkdownOutput(summary, badgeUrl, o.Indicators, o.HideBranchRate, o.HideComplexity);
if (o.FailBelowThreshold) if (o.FailBelowThreshold)
output += $"{Environment.NewLine}_Minimum allowed line rate is `{lowerThreshold * 100:N0}%`_{Environment.NewLine}"; output += $"{Environment.NewLine}_Minimum allowed line rate is `{lowerThreshold * 100:N0}%`_{Environment.NewLine}";
} }
@@ -103,9 +114,8 @@ namespace CodeCoverageSummary
_ => -1); // invalid arguments _ => -1); // invalid arguments
} }
private static CodeSummary ParseTestResults(string filename) private static CodeSummary ParseTestResults(string filename, CodeSummary summary)
{ {
CodeSummary summary = new();
try try
{ {
string rss = File.ReadAllText(filename); string rss = File.ReadAllText(filename);
@@ -118,34 +128,32 @@ namespace CodeCoverageSummary
var lineR = from item in coverage.Attributes() var lineR = from item in coverage.Attributes()
where item.Name == "line-rate" where item.Name == "line-rate"
select item; select item;
summary.LineRate = double.Parse(lineR.First().Value); summary.LineRate += double.Parse(lineR.First().Value);
var linesCovered = from item in coverage.Attributes() var linesCovered = from item in coverage.Attributes()
where item.Name == "lines-covered" where item.Name == "lines-covered"
select item; select item;
summary.LinesCovered = int.Parse(linesCovered.First().Value); summary.LinesCovered += int.Parse(linesCovered.First().Value);
var linesValid = from item in coverage.Attributes() var linesValid = from item in coverage.Attributes()
where item.Name == "lines-valid" where item.Name == "lines-valid"
select item; select item;
summary.LinesValid = int.Parse(linesValid.First().Value); summary.LinesValid += int.Parse(linesValid.First().Value);
var branchR = from item in coverage.Attributes() var branchR = from item in coverage.Attributes()
where item.Name == "branch-rate" where item.Name == "branch-rate"
select item; select item;
summary.BranchRate = double.Parse(branchR.First().Value); summary.BranchRate += double.Parse(branchR.First().Value);
var branchesCovered = from item in coverage.Attributes() var branchesCovered = from item in coverage.Attributes()
where item.Name == "branches-covered" where item.Name == "branches-covered"
select item; select item;
summary.BranchesCovered = int.Parse(branchesCovered.First().Value); summary.BranchesCovered += int.Parse(branchesCovered.First().Value);
var branchesValid = from item in coverage.Attributes() var branchesValid = from item in coverage.Attributes()
where item.Name == "branches-valid" where item.Name == "branches-valid"
select item; select item;
summary.BranchesValid = int.Parse(branchesValid.First().Value); summary.BranchesValid += int.Parse(branchesValid.First().Value);
summary.Complexity = 0;
// test coverage for individual packages // test coverage for individual packages
var packages = from item in coverage.Descendants("package") var packages = from item in coverage.Descendants("package")
@@ -244,7 +252,7 @@ namespace CodeCoverageSummary
} }
} }
private static string GenerateTextOutput(CodeSummary summary, string badgeUrl, bool indicators) private static string GenerateTextOutput(CodeSummary summary, string badgeUrl, bool indicators, bool hideBranchRate, bool hideComplexity)
{ {
StringBuilder textOutput = new(); StringBuilder textOutput = new();
@@ -257,20 +265,20 @@ namespace CodeCoverageSummary
foreach (CodeCoverage package in summary.Packages) foreach (CodeCoverage package in summary.Packages)
{ {
textOutput.Append($"{package.Name}: Line Rate = {package.LineRate * 100:N0}%") textOutput.Append($"{package.Name}: Line Rate = {package.LineRate * 100:N0}%")
.Append($", Branch Rate = {package.BranchRate * 100:N0}%") .Append(hideBranchRate ? string.Empty : $", Branch Rate = {package.BranchRate * 100:N0}%")
.Append((package.Complexity % 1 == 0) ? $", Complexity = {package.Complexity}" : $", Complexity = {package.Complexity:N4}") .Append(hideComplexity ? string.Empty : (package.Complexity % 1 == 0) ? $", Complexity = {package.Complexity}" : $", Complexity = {package.Complexity:N4}")
.AppendLine(indicators ? $", {GenerateHealthIndicator(package.LineRate)}" : string.Empty); .AppendLine(indicators ? $", {GenerateHealthIndicator(package.LineRate)}" : string.Empty);
} }
textOutput.Append($"Summary: Line Rate = {summary.LineRate * 100:N0}% ({summary.LinesCovered} / {summary.LinesValid})") textOutput.Append($"Summary: Line Rate = {summary.LineRate * 100:N0}% ({summary.LinesCovered} / {summary.LinesValid})")
.Append($", Branch Rate = {summary.BranchRate * 100:N0}% ({summary.BranchesCovered} / {summary.BranchesValid})") .Append(hideBranchRate ? string.Empty : $", Branch Rate = {summary.BranchRate * 100:N0}% ({summary.BranchesCovered} / {summary.BranchesValid})")
.Append((summary.Complexity % 1 == 0) ? $", Complexity = {summary.Complexity}" : $", Complexity = {summary.Complexity:N4}") .Append(hideComplexity ? string.Empty : (summary.Complexity % 1 == 0) ? $", Complexity = {summary.Complexity}" : $", Complexity = {summary.Complexity:N4}")
.AppendLine(indicators ? $", {GenerateHealthIndicator(summary.LineRate)}" : string.Empty); .AppendLine(indicators ? $", {GenerateHealthIndicator(summary.LineRate)}" : string.Empty);
return textOutput.ToString(); return textOutput.ToString();
} }
private static string GenerateMarkdownOutput(CodeSummary summary, string badgeUrl, bool indicators) private static string GenerateMarkdownOutput(CodeSummary summary, string badgeUrl, bool indicators, bool hideBranchRate, bool hideComplexity)
{ {
StringBuilder markdownOutput = new(); StringBuilder markdownOutput = new();
@@ -280,22 +288,26 @@ namespace CodeCoverageSummary
.AppendLine(); .AppendLine();
} }
markdownOutput.Append("Package | Line Rate | Branch Rate | Complexity") markdownOutput.Append("Package | Line Rate")
.Append(hideBranchRate ? string.Empty : " | Branch Rate")
.Append(hideComplexity ? string.Empty : " | Complexity")
.AppendLine(indicators ? " | Health" : string.Empty) .AppendLine(indicators ? " | Health" : string.Empty)
.Append("-------- | --------- | ----------- | ----------") .Append("-------- | ---------")
.Append(hideBranchRate ? string.Empty : " | -----------")
.Append(hideComplexity ? string.Empty : " | ----------")
.AppendLine(indicators ? " | ------" : string.Empty); .AppendLine(indicators ? " | ------" : string.Empty);
foreach (CodeCoverage package in summary.Packages) foreach (CodeCoverage package in summary.Packages)
{ {
markdownOutput.Append($"{package.Name} | {package.LineRate * 100:N0}%") markdownOutput.Append($"{package.Name} | {package.LineRate * 100:N0}%")
.Append($" | {package.BranchRate * 100:N0}%") .Append(hideBranchRate ? string.Empty : $" | {package.BranchRate * 100:N0}%")
.Append((package.Complexity % 1 == 0) ? $" | {package.Complexity}" : $" | {package.Complexity:N4}" ) .Append(hideComplexity ? string.Empty : (package.Complexity % 1 == 0) ? $" | {package.Complexity}" : $" | {package.Complexity:N4}" )
.AppendLine(indicators ? $" | {GenerateHealthIndicator(package.LineRate)}" : string.Empty); .AppendLine(indicators ? $" | {GenerateHealthIndicator(package.LineRate)}" : string.Empty);
} }
markdownOutput.Append($"**Summary** | **{summary.LineRate * 100:N0}%** ({summary.LinesCovered} / {summary.LinesValid})") markdownOutput.Append($"**Summary** | **{summary.LineRate * 100:N0}%** ({summary.LinesCovered} / {summary.LinesValid})")
.Append($" | **{summary.BranchRate * 100:N0}%** ({summary.BranchesCovered} / {summary.BranchesValid})") .Append(hideBranchRate ? string.Empty : $" | **{summary.BranchRate * 100:N0}%** ({summary.BranchesCovered} / {summary.BranchesValid})")
.Append((summary.Complexity % 1 == 0) ? $" | {summary.Complexity}" : $" | {summary.Complexity:N4}") .Append(hideComplexity ? string.Empty : (summary.Complexity % 1 == 0) ? $" | **{summary.Complexity}**" : $" | **{summary.Complexity:N4}**")
.AppendLine(indicators ? $" | {GenerateHealthIndicator(summary.LineRate)}" : string.Empty); .AppendLine(indicators ? $" | {GenerateHealthIndicator(summary.LineRate)}" : string.Empty);
return markdownOutput.ToString(); return markdownOutput.ToString();
@@ -2,11 +2,11 @@
"profiles": { "profiles": {
"CodeCoverageSummary": { "CodeCoverageSummary": {
"commandName": "Project", "commandName": "Project",
"commandLineArgs": "../../../../coverage.cobertura.xml --format=md --badge true --thresholds=\"85 90\" --fail true" "commandLineArgs": "--files ../../../../coverage.cobertura.xml,../../../../coverage.cobertura.xml --format=text --badge true --thresholds=\"85 90\" --fail true"
}, },
"Docker": { "Docker": {
"commandName": "Docker", "commandName": "Docker",
"commandLineArgs": "/src/coverage.cobertura.xml --format=md --badge=true" "commandLineArgs": "--files /app/sample.coverage.xml --format=text --badge=true"
} }
} }
} }