|
| 1 | +--- |
| 2 | +name: 'Upload Code Coverage with Tools' |
| 3 | +description: 'Upload results with various Code Coverage tools' |
| 4 | +author: 'Mr. Walls' |
| 5 | +branding: |
| 6 | + icon: 'upload' |
| 7 | + color: 'green' |
| 8 | +inputs: |
| 9 | + tests-outcome: |
| 10 | + description: | |
| 11 | + The result outcome of the test that generated the coverage results. |
| 12 | + type: choice |
| 13 | + options: |
| 14 | + - cancelled |
| 15 | + - failure |
| 16 | + - neutral |
| 17 | + - success |
| 18 | + - skipped |
| 19 | + - timed_out |
| 20 | + required: true |
| 21 | + job_code: |
| 22 | + description: | |
| 23 | + The job-id of the test that generated the coverage results. |
| 24 | + required: true |
| 25 | + python-version: |
| 26 | + description: | |
| 27 | + The python version to use. The default is to use the value of the environment |
| 28 | + variable 'PYTHON_VERSION'. |
| 29 | + default: '3.12' |
| 30 | + required: true |
| 31 | + os: |
| 32 | + description: | |
| 33 | + When running this action on github.com, the default value is sufficient. When running on |
| 34 | + GHES, you can pass the 'unknown' value to override this. |
| 35 | + default: ${{ github.server_url == 'https://github.com' && runner.os || 'unknown' }} |
| 36 | + type: choice |
| 37 | + options: |
| 38 | + - unknown |
| 39 | + - macOS |
| 40 | + - Linux |
| 41 | + - Windows |
| 42 | + required: true |
| 43 | +secrets: |
| 44 | + codeclimate-token: |
| 45 | + description: | |
| 46 | + The token used to authenticate when performing codeclimate API operations. |
| 47 | + default: '' |
| 48 | + required: true |
| 49 | + cc-test-reporter-id: |
| 50 | + description: | |
| 51 | + The id used to report tests when performing codeclimate API operations. |
| 52 | + default: '' |
| 53 | + required: true |
| 54 | + deepsource-dsn: |
| 55 | + description: | |
| 56 | + The deepsource DSN when performing deepsource API operations. |
| 57 | + default: '' |
| 58 | + required: true |
| 59 | + coveralls-token: |
| 60 | + description: | |
| 61 | + The coveralls token used when performing coveralls API operations. |
| 62 | + default: '' |
| 63 | + required: true |
| 64 | + codecov-token: |
| 65 | + description: | |
| 66 | + The codecov token used when performing codecov API operations. |
| 67 | + default: '' |
| 68 | + required: true |
| 69 | +outputs: |
| 70 | + sha: |
| 71 | + description: "The SHA of the commit checked-out" |
| 72 | + value: ${{ steps.output_sha.outputs.sha || 'HEAD' }} |
| 73 | + can_upload: |
| 74 | + description: "Can the upload tool even be used?" |
| 75 | + value: ${{ steps.output_can_upload.outputs.can_upload || 'false' }} |
| 76 | + status: |
| 77 | + description: "The outcome of the coverage test reporter action." |
| 78 | + value: ${{ steps.coverage_outcome.outputs.status || 'cancelled' }} |
| 79 | + coverage_upload_codecov_outcome: |
| 80 | + value: ${{ steps.coverage-codecov-upload.outcome || 'cancelled' }} |
| 81 | + coverage_upload_codeclimate_outcome: |
| 82 | + value: ${{ steps.coverage-codeclimate-upload.outcome || 'cancelled' }} |
| 83 | + coverage_upload_deepsource_outcome: |
| 84 | + value: ${{ steps.coverage-deepsource-upload.outcome || 'cancelled' }} |
| 85 | + coverage_upload_coveralls_outcome: |
| 86 | + value: ${{ steps.coverage-coveralls-upload.outcome || 'cancelled' }} |
| 87 | + coverage_upload_artifact_outcome: |
| 88 | + value: ${{ steps.coverage-reports-upload.outcome || 'cancelled' }} |
| 89 | + |
| 90 | +runs: |
| 91 | + using: composite |
| 92 | + steps: |
| 93 | + - name: "Calculate Commit SHA" |
| 94 | + id: output_sha |
| 95 | + shell: bash |
| 96 | + run: | |
| 97 | + printf "sha=%s\n" $(git rev-parse --verify HEAD) >> "$GITHUB_OUTPUT" |
| 98 | + printf "BUILD_SHA=%s\n" $(git rev-parse --verify HEAD) >> "$GITHUB_ENV" |
| 99 | + - name: "Identify Python Version" |
| 100 | + id: output_python |
| 101 | + if: ${{ !cancelled() }} |
| 102 | + env: |
| 103 | + PYTHON_VERSION_INPUT: ${{ inputs.python-version }} |
| 104 | + shell: bash |
| 105 | + run: | |
| 106 | + if [[ -n $PYTHON_VERSION_INPUT ]]; then |
| 107 | + printf "python-version=%s\n" "${PYTHON_VERSION_INPUT}" >> "$GITHUB_OUTPUT" |
| 108 | + PYTHON_VERSION=${PYTHON_VERSION_INPUT} |
| 109 | + else |
| 110 | + printf "python-version=%s\n" "${PYTHON_VERSION}" >> "$GITHUB_OUTPUT" |
| 111 | + fi |
| 112 | + printf "%s\n" "PYTHON_VERSION=${PYTHON_VERSION}" >> "$GITHUB_ENV" |
| 113 | + - name: "Identify Operating System" |
| 114 | + id: output_os |
| 115 | + if: ${{ !cancelled() }} |
| 116 | + env: |
| 117 | + OS_INPUT: ${{ inputs.os }} |
| 118 | + shell: bash |
| 119 | + run: | |
| 120 | + if [[ -n $OS_INPUT ]]; then |
| 121 | + printf "os=%s\n" "${OS_INPUT}" >> "$GITHUB_OUTPUT" |
| 122 | + OS=${OS_INPUT} |
| 123 | + else |
| 124 | + printf "os=%s\n" "${OS:-unknown}" >> "$GITHUB_OUTPUT" |
| 125 | + fi |
| 126 | + printf "%s\n" "OS=${OS}" >> "$GITHUB_ENV" |
| 127 | + - name: "Prepare Artifact Name" |
| 128 | + id: output_artifact_name |
| 129 | + if: ${{ !cancelled() }} |
| 130 | + shell: bash |
| 131 | + run: | |
| 132 | + if [[ "${OS}" != "Windows" ]] ; then |
| 133 | + printf "artifact-name=%s\n" multicast-coverage-${BUILD_SHA}-part-$(uuidgen) >> "$GITHUB_OUTPUT" |
| 134 | + else |
| 135 | + printf "artifact-name=%s" multicast-coverage-${BUILD_SHA}-part- >> "$GITHUB_OUTPUT" |
| 136 | + printf "%04x%04x-%04x-%04x-%04x-%04x%04x%04x\n" $RANDOM $RANDOM $RANDOM $(($RANDOM & 0x0fff | 0x4000)) $(($RANDOM & 0x3fff | 0x8000)) $RANDOM $RANDOM $RANDOM >> "$GITHUB_OUTPUT" |
| 137 | + fi |
| 138 | + printf "%s\n" "COV_STEP_SUMMARY=Coverage-Summary-Artifact-${OS}-${PYTHON_VERSION}.txt" >> "$GITHUB_ENV" |
| 139 | + - name: "Check has upload Tools" |
| 140 | + id: output_upload_tools |
| 141 | + if: ${{ !cancelled() }} |
| 142 | + shell: bash |
| 143 | + run: | |
| 144 | + if [[ "${OS}" != "unknown" ]] ; then |
| 145 | + if [[ -x ${{ github.workspace }}/cc-test-reporter ]] ; then |
| 146 | + printf "can_upload_to_codeclimate=true\n" >> "$GITHUB_OUTPUT" |
| 147 | + printf "::debug::%s\n" "Found ${{ github.workspace }}/cc-test-reporter" |
| 148 | + else |
| 149 | + printf "::warning, title='Missing tool':: %s\n" "Can't find cc-test-reporter tool." |
| 150 | + printf "can_upload_to_codeclimate=false\n" >> "$GITHUB_OUTPUT" |
| 151 | + fi |
| 152 | + LOCALBIN="${{ github.workspace }}/bin" |
| 153 | + if [[ -d "${BINDIR:-${LOCALBIN}}" ]] ; then |
| 154 | + printf "::debug::%s\n" "Found ${BINDIR:-${LOCALBIN}}" |
| 155 | + if [[ -x "${BINDIR:-${LOCALBIN}}/coveralls" ]] ; then |
| 156 | + printf "can_upload_to_coveralls=true\n" >> "$GITHUB_OUTPUT" |
| 157 | + printf "::debug::%s\n" "Found ${BINDIR:-${LOCALBIN}}/coveralls" |
| 158 | + printf "coveralls_executable=${BINDIR:-${LOCALBIN}}/coveralls\n" >> "$GITHUB_OUTPUT" |
| 159 | + else |
| 160 | + printf "::warning, title='Missing tool':: %s\n" "Can't find coveralls tool." |
| 161 | + printf "can_upload_to_coveralls=false\n" >> "$GITHUB_OUTPUT" |
| 162 | + fi |
| 163 | + if [[ -x "${BINDIR:-${LOCALBIN}}/deepsource" ]] ; then |
| 164 | + printf "can_upload_to_deepsource=true\n" >> "$GITHUB_OUTPUT" |
| 165 | + printf "::debug::%s\n" "Found ${BINDIR:-${LOCALBIN}}/deepsource" |
| 166 | + printf "deepsource_executable=%s\n" "${BINDIR:-${LOCALBIN}}/deepsource" >> "$GITHUB_OUTPUT" |
| 167 | + else |
| 168 | + printf "::warning, title='Missing tool':: %s\n" "Can't find deepsource tool." |
| 169 | + printf "can_upload_to_deepsource=false\n" >> "$GITHUB_OUTPUT" |
| 170 | + fi |
| 171 | + else |
| 172 | + printf "::notice, title='Missing BINDIR':: %s\n" "Can't find ${BINDIR:-${LOCALBIN}}." |
| 173 | + if [[ -x $(command -v coveralls) ]] ; then |
| 174 | + printf "can_upload_to_coveralls=true\n" >> "$GITHUB_OUTPUT" |
| 175 | + printf "::debug::%s %s\n" "Found" $(command -v coveralls) |
| 176 | + printf "coveralls_executable=%s\n" $(command -v coveralls) >> "$GITHUB_OUTPUT" |
| 177 | + else |
| 178 | + printf "::warning, title='Missing tool':: %s\n" "Can't find coveralls tool." |
| 179 | + printf "can_upload_to_coveralls=false\n" >> "$GITHUB_OUTPUT" |
| 180 | + printf "coveralls_executable=coveralls\n" >> "$GITHUB_OUTPUT" |
| 181 | + fi |
| 182 | + if [[ -x $(command -v deepsource) ]] ; then |
| 183 | + printf "can_upload_to_deepsource=true\n" >> "$GITHUB_OUTPUT" |
| 184 | + printf "::debug::%s %s\n" "Found" $(command -v deepsource) |
| 185 | + printf "deepsource_executable=%s\n" $(command -v deepsource) >> "$GITHUB_OUTPUT" |
| 186 | + else |
| 187 | + printf "::warning, title='Missing tool':: %s\n" "Can't find deepsource tool." |
| 188 | + printf "can_upload_to_deepsource=false\n" >> "$GITHUB_OUTPUT" |
| 189 | + printf "deepsource_executable=deepsource\n" >> "$GITHUB_OUTPUT" |
| 190 | + fi |
| 191 | + fi |
| 192 | + else |
| 193 | + printf "::warning, title='Missing tools':: %s\n" "Can't find any supported tool." |
| 194 | + printf "can_upload_to_codeclimate=false\n" >> "$GITHUB_OUTPUT" |
| 195 | + printf "can_upload_to_coveralls=false\n" >> "$GITHUB_OUTPUT" |
| 196 | + printf "can_upload_to_deepsource=false\n" >> "$GITHUB_OUTPUT" |
| 197 | + fi |
| 198 | + if [[ -d "${{ github.workspace }}/test-reports" ]] ; then |
| 199 | + if [[ ( -x $(command -v coverage3) ) ]] ; then |
| 200 | + printf "\n" >> "${GITHUB_STEP_SUMMARY}" |
| 201 | + coverage3 combine --keep --data-file=coverage_codecov ./.coverage.* 2>/dev/null || true |
| 202 | + wait ; |
| 203 | + coverage3 report -m --include=multicast/* --ignore-errors --data-file=coverage_codecov 2>/dev/null >> "${GITHUB_STEP_SUMMARY}" || true |
| 204 | + if [[ ! ( -f "${{ github.workspace }}/test-reports/coverage.xml" ) ]] ; then |
| 205 | + coverage3 xml -o "${{ github.workspace }}/test-reports/coverage.xml" --data-file=coverage_codecov --include=multicast/* 2>/dev/null || true |
| 206 | + fi ; |
| 207 | + rm -f coverage_codecov 2>/dev/null || true ; wait ; |
| 208 | + printf "\n\n" >> "${GITHUB_STEP_SUMMARY}" |
| 209 | + elif [[ ( -x $(command -v coverage) ) ]] ; then |
| 210 | + coverage combine --keep --data-file=coverage_codecov ./.coverage.* 2>/dev/null || true |
| 211 | + wait ; |
| 212 | + coverage report -m --include=multicast/* --ignore-errors --data-file=coverage_codecov --format=markdown 2>/dev/null >> "${GITHUB_STEP_SUMMARY}" || true |
| 213 | + if [[ ! ( -f "${{ github.workspace }}/test-reports/coverage.xml" ) ]] ; then |
| 214 | + coverage xml -o "${{ github.workspace }}/test-reports/coverage.xml" --include=multicast/* --data-file=coverage_codecov 2>/dev/null || true |
| 215 | + fi ; |
| 216 | + rm -f coverage_codecov 2>/dev/null || true ; wait ; |
| 217 | + printf "\n\n" >> "${GITHUB_STEP_SUMMARY}" |
| 218 | + fi |
| 219 | + if [[ -f "${{ github.workspace }}/test-reports/coverage.xml" ]] ; then |
| 220 | + printf "can_upload_to_codecov=true\n" >> "$GITHUB_OUTPUT" |
| 221 | + else |
| 222 | + printf "can_upload_to_codecov=false\n" >> "$GITHUB_OUTPUT" |
| 223 | + fi |
| 224 | + else |
| 225 | + printf "can_upload_to_codecov=false\n" >> "$GITHUB_OUTPUT" |
| 226 | + fi |
| 227 | + - name: "Check can upload" |
| 228 | + id: output_can_upload |
| 229 | + if: ${{ !cancelled() }} |
| 230 | + shell: bash |
| 231 | + run: | |
| 232 | + if [[ "${OS}" != "unknown" ]] ; then |
| 233 | + if [[ "${{ steps.output_upload_tools.outputs.can_upload_to_codecov }}" == "true" ]] || \ |
| 234 | + [[ "${{ steps.output_upload_tools.outputs.can_upload_to_codeclimate }}" == "true" ]] || \ |
| 235 | + [[ "${{ steps.output_upload_tools.outputs.can_upload_to_coveralls }}" == "true" ]] || \ |
| 236 | + [[ "${{ steps.output_upload_tools.outputs.can_upload_to_deepsource }}" == "true" ]] ; then |
| 237 | + THE_RESULT="success" |
| 238 | + else |
| 239 | + THE_RESULT="failure" |
| 240 | + fi |
| 241 | + else |
| 242 | + THE_RESULT="skipped" |
| 243 | + fi |
| 244 | + printf "status=%s\n" "${THE_RESULT}" >> "$GITHUB_OUTPUT" |
| 245 | + if [[ "${THE_RESULT}" == "success" ]] ; then |
| 246 | + printf "can_upload=true\n" >> "$GITHUB_OUTPUT" |
| 247 | + exit 0 |
| 248 | + else |
| 249 | + printf "can_upload=false\n" >> "$GITHUB_OUTPUT" |
| 250 | + if [[ "${THE_RESULT}" == "failure" ]] ; then |
| 251 | + exit 1 |
| 252 | + fi ; |
| 253 | + fi |
| 254 | + - name: check codecov config |
| 255 | + id: check-codecov-config |
| 256 | + if: ${{ !cancelled() && (steps.output_can_upload.outputs.can_upload == 'true') && (steps.output_upload_tools.outputs.can_upload_to_codecov == 'true') }} |
| 257 | + shell: bash |
| 258 | + run: | |
| 259 | + ${{ github.workspace }}/tests/check_codecov || exit 1 |
| 260 | + - name: Upload ${{ steps.output_os.outputs.os }} Python ${{ steps.output_python.outputs.python-version }} coverage to Codecov |
| 261 | + id: coverage-codecov-upload |
| 262 | + if: ${{ success() && (steps.output_can_upload.outputs.can_upload == 'true') && (steps.output_upload_tools.outputs.can_upload_to_codecov == 'true') }} |
| 263 | + uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 |
| 264 | + with: |
| 265 | + token: ${{ secrets.codecov-token }} |
| 266 | + job_code: ${{ inputs.job_code || '' }} |
| 267 | + override_commit: ${{ steps.output_sha.outputs.sha }} |
| 268 | + files: ./coverage.xml,./test-reports/coverage.xml |
| 269 | + directory: ${{ github.workspace }} |
| 270 | + flags: multicast |
| 271 | + name: multicast-github-${{ steps.output_os.outputs.os }}-${{ steps.output_python.outputs.python-version }}-${{ steps.output_sha.outputs.sha }} |
| 272 | + verbose: true |
| 273 | + fail_ci_if_error: false |
| 274 | + - name: Upload ${{ steps.output_os.outputs.os }} Python ${{ steps.output_python.outputs.python-version }} Artifact |
| 275 | + id: coverage-reports-upload |
| 276 | + if: ${{ !cancelled() && (steps.output_can_upload.outputs.can_upload == 'true') }} |
| 277 | + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 |
| 278 | + with: |
| 279 | + name: Test-Report-${{ steps.output_os.outputs.os }}-${{ steps.output_python.outputs.python-version }}-${{ steps.output_sha.outputs.sha }} |
| 280 | + path: ./test-reports/ |
| 281 | + if-no-files-found: ignore |
| 282 | + compression-level: 9 |
| 283 | + retention-days: 2 |
| 284 | + overwrite: true |
| 285 | + - name: Upload ${{ steps.output_os.outputs.os }} Python ${{ steps.output_python.outputs.python-version }} coverage to code-climate |
| 286 | + if: ${{ !cancelled() && (steps.output_can_upload.outputs.can_upload == 'true') && (steps.output_upload_tools.outputs.can_upload_to_codeclimate == 'true') && (steps.output_os.outputs.os != 'Windows') && (github.repository == 'reactive-firewall/multicast') }} |
| 287 | + id: coverage-codeclimate-upload |
| 288 | + shell: bash |
| 289 | + env: |
| 290 | + CODECLIMATE_REPO_TOKEN: ${{ github.server_url == 'https://github.com' && secrets.codeclimate-token || '' }} |
| 291 | + CC_TEST_REPORTER_ID: ${{ github.server_url == 'https://github.com' && secrets.cc-test-reporter-id || '' }} |
| 292 | + run: | |
| 293 | + if [[ "${{ inputs.tests-outcome }}" == "success" ]] ; then |
| 294 | + ./cc-test-reporter after-build --exit-code 0 || exit 1 ; |
| 295 | + else |
| 296 | + ./cc-test-reporter after-build --exit-code 1 || exit 1 ; |
| 297 | + fi |
| 298 | + - name: Upload ${{ steps.output_os.outputs.os }} Python ${{ steps.output_python.outputs.python-version }} coverage to deepsource |
| 299 | + if: ${{ !cancelled() && (steps.output_can_upload.outputs.can_upload == 'true') && (steps.output_upload_tools.outputs.can_upload_to_deepsource == 'true') && (steps.output_os.outputs.os != 'Windows') && (github.repository == 'reactive-firewall/multicast') }} |
| 300 | + id: coverage-deepsource-upload |
| 301 | + shell: bash |
| 302 | + env: |
| 303 | + DEEPSOURCE_DSN: ${{ github.server_url == 'https://github.com' && secrets.deepsource-dsn || '' }} |
| 304 | + DEEPSOURCE_TOOL: ${{ steps.output_upload_tools.outputs.deepsource_executable }} |
| 305 | + run: | |
| 306 | + ${DEEPSOURCE_TOOL} report --analyzer test-coverage --key python --value-file ./coverage.xml 2>/dev/null |
| 307 | + - name: Upload ${{ steps.output_os.outputs.os }} Python ${{ steps.output_python.outputs.python-version }} coverage to coveralls |
| 308 | + if: ${{ !cancelled() && (steps.output_can_upload.outputs.can_upload == 'true') && (steps.output_upload_tools.outputs.can_upload_to_coveralls == 'true') && (steps.output_os.outputs.os != 'Windows') && (github.repository == 'reactive-firewall/multicast') }} |
| 309 | + id: coverage-coveralls-upload |
| 310 | + shell: bash |
| 311 | + env: |
| 312 | + COVERALLS_REPO_TOKEN: ${{ github.server_url == 'https://github.com' && secrets.coveralls-token || '' }} |
| 313 | + COVERALLS_TOOL: ${{ steps.output_upload_tools.outputs.coveralls_executable }} |
| 314 | + run: | |
| 315 | + if [[ "${{ inputs.tests-outcome }}" == "success" ]] ; then |
| 316 | + ${COVERALLS_TOOL} report test-reports/coverage.xml --base-path="${{ github.workspace }}" --format=python --service-job-id=${{ github.run_id }} --parallel --job-flag='${{ steps.output_os.outputs.os }}-${{ steps.output_python.outputs.python-version }}' --build-number=${{ inputs.job_code }} || exit 1 ; |
| 317 | + else |
| 318 | + ${COVERALLS_TOOL} report test-reports/coverage.xml --base-path="${{ github.workspace }}" --allow-empty --format=python --service-job-id=${{ github.run_id }} --parallel --job-flag='${{ steps.output_os.outputs.os }}-${{ steps.output_python.outputs.python-version }}' --build-number=${{ inputs.job_code }} || exit 1 ; |
| 319 | + fi |
| 320 | + - name: "Evaluate Coverage Report Task" |
| 321 | + id: coverage_outcome |
| 322 | + if: ${{ !cancelled() }} |
| 323 | + shell: bash |
| 324 | + run: | |
| 325 | + if [[ "${{ steps.output_can_upload.outputs.can_upload }}" == "true" ]] ; then |
| 326 | + if [[ "${{ inputs.tests-outcome }}" == "success" ]] ; then |
| 327 | + THE_RESULT="success" |
| 328 | + else |
| 329 | + THE_RESULT="neutral" |
| 330 | + fi ; |
| 331 | + else |
| 332 | + THE_RESULT="skipped" |
| 333 | + fi |
| 334 | + printf "status=%s\n" "${THE_RESULT}" >> "$GITHUB_OUTPUT" |
| 335 | + if [[ "${THE_RESULT}" == "failure" ]] ; then |
| 336 | + exit 1 |
| 337 | + else |
| 338 | + exit 0 |
| 339 | + fi |
0 commit comments