diff --git a/.gitea/workflows/pr-validation.yml b/.gitea/workflows/pr-validation.yml index 106a130..b0b8bc1 100644 --- a/.gitea/workflows/pr-validation.yml +++ b/.gitea/workflows/pr-validation.yml @@ -10,6 +10,14 @@ on: jobs: validate: runs-on: ubuntu-latest + env: + ARTEFACT_BUCKET_NAME: ${{ vars.ARTEFACT_BUCKET_NAME }} + ARTEFACT_BUCKET_ENDPONT: ${{ vars.ARTEFACT_BUCKET_ENDPONT }} + ARTEFACT_BUCKET_REGION: ${{ vars.ARTEFACT_BUCKET_REGION }} + AWS_ACCESS_KEY_ID: ${{ secrets.ARTEFACT_BUCKET_WRITE_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.ARTEFACT_BUCKET_WRITE_ACCESS_SECRET }} + AWS_DEFAULT_REGION: ${{ vars.ARTEFACT_BUCKET_REGION }} + AWS_EC2_METADATA_DISABLED: true steps: - name: Checkout uses: actions/checkout@v4 @@ -19,8 +27,134 @@ jobs: with: go-version-file: go.mod - - name: Run full unit test suite - run: go test ./... + - name: Ensure tooling is available + run: | + set -euo pipefail + + if ! command -v aws >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y awscli jq + exit 0 + fi + + if ! command -v jq >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y jq + fi + + - name: Run full unit test suite with coverage + id: coverage + run: | + set -euo pipefail + + go test -covermode=atomic -coverprofile=coverage.out ./... + go tool cover -html=coverage.out -o coverage.html + + total="$(go tool cover -func=coverage.out | awk '/^total:/ {sub(/%/, "", $3); print $3}')" + printf '{\n "total": "%s"\n}\n' "$total" > coverage-summary.json + printf 'total=%s\n' "$total" >> "$GITHUB_OUTPUT" + + - name: Generate coverage badge + env: + COVERAGE_TOTAL: ${{ steps.coverage.outputs.total }} + run: | + set -euo pipefail + + color="$(awk -v total="$COVERAGE_TOTAL" 'BEGIN { + if (total >= 80) print "brightgreen"; + else if (total >= 70) print "green"; + else if (total >= 60) print "yellowgreen"; + else if (total >= 50) print "yellow"; + else print "red"; + }')" + + cat > coverage-badge.svg < + + + + + + + + + + + + + + coverage + coverage + ${COVERAGE_TOTAL}% + ${COVERAGE_TOTAL}% + + + EOF + + - name: Upload PR coverage artefacts + id: upload + run: | + set -euo pipefail + + aws configure set default.s3.addressing_style path + + repo_name="${GITHUB_REPOSITORY##*/}" + prefix="${repo_name}/pull-requests/${{ github.event.pull_request.number }}" + report_url="${ARTEFACT_BUCKET_ENDPONT%/}/${ARTEFACT_BUCKET_NAME}/${prefix}/coverage.html" + badge_url="${ARTEFACT_BUCKET_ENDPONT%/}/${ARTEFACT_BUCKET_NAME}/${prefix}/coverage-badge.svg" + + aws --endpoint-url "${ARTEFACT_BUCKET_ENDPONT}" s3 cp coverage.html "s3://${ARTEFACT_BUCKET_NAME}/${prefix}/coverage.html" --content-type text/html + aws --endpoint-url "${ARTEFACT_BUCKET_ENDPONT}" s3 cp coverage-badge.svg "s3://${ARTEFACT_BUCKET_NAME}/${prefix}/coverage-badge.svg" --content-type image/svg+xml + aws --endpoint-url "${ARTEFACT_BUCKET_ENDPONT}" s3 cp coverage-summary.json "s3://${ARTEFACT_BUCKET_NAME}/${prefix}/coverage-summary.json" --content-type application/json + + printf 'report_url=%s\n' "$report_url" >> "$GITHUB_OUTPUT" + printf 'badge_url=%s\n' "$badge_url" >> "$GITHUB_OUTPUT" + + - name: Comment coverage report on pull request + env: + COVERAGE_BADGE_URL: ${{ steps.upload.outputs.badge_url }} + COVERAGE_REPORT_URL: ${{ steps.upload.outputs.report_url }} + COVERAGE_TOTAL: ${{ steps.coverage.outputs.total }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -euo pipefail + + marker='' + api_base="${GITHUB_SERVER_URL}/api/v1/repos/${GITHUB_REPOSITORY}" + + payload="$(jq -n \ + --arg marker "$marker" \ + --arg total "$COVERAGE_TOTAL" \ + --arg report "$COVERAGE_REPORT_URL" \ + --arg badge "$COVERAGE_BADGE_URL" \ + '{body: ($marker + "\n## Coverage Report\n\nCoverage total: **" + $total + "%**\n\n[HTML report](" + $report + ")\n\n![Coverage badge](" + $badge + ")")}')" + + comments="$(curl -sS -H "Authorization: token ${GITHUB_TOKEN}" "${api_base}/issues/${{ github.event.pull_request.number }}/comments")" + comment_id="$(printf '%s' "$comments" | jq -r '.[] | select(.body | contains("")) | .id' | tail -n 1)" + + if [[ -n "$comment_id" ]]; then + curl -sS -X PATCH \ + -H "Authorization: token ${GITHUB_TOKEN}" \ + -H 'Content-Type: application/json' \ + -d "$payload" \ + "${api_base}/issues/comments/${comment_id}" >/dev/null + else + curl -sS -X POST \ + -H "Authorization: token ${GITHUB_TOKEN}" \ + -H 'Content-Type: application/json' \ + -d "$payload" \ + "${api_base}/issues/${{ github.event.pull_request.number }}/comments" >/dev/null + fi + + - name: Add coverage summary + run: | + { + echo '## Coverage' + echo + echo '- Total: `${{ steps.coverage.outputs.total }}%`' + echo '- Report: ${{ steps.upload.outputs.report_url }}' + echo '- Badge: ${{ steps.upload.outputs.badge_url }}' + } >> "$GITHUB_STEP_SUMMARY" - name: Run behavior suite run: ./script/run-behavior-suite-docker.sh diff --git a/.gitea/workflows/push-validation.yml b/.gitea/workflows/push-validation.yml index e8e4996..16df285 100644 --- a/.gitea/workflows/push-validation.yml +++ b/.gitea/workflows/push-validation.yml @@ -10,6 +10,14 @@ on: jobs: validate: runs-on: ubuntu-latest + env: + ARTEFACT_BUCKET_NAME: ${{ vars.ARTEFACT_BUCKET_NAME }} + ARTEFACT_BUCKET_ENDPONT: ${{ vars.ARTEFACT_BUCKET_ENDPONT }} + ARTEFACT_BUCKET_REGION: ${{ vars.ARTEFACT_BUCKET_REGION }} + AWS_ACCESS_KEY_ID: ${{ secrets.ARTEFACT_BUCKET_WRITE_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.ARTEFACT_BUCKET_WRITE_ACCESS_SECRET }} + AWS_DEFAULT_REGION: ${{ vars.ARTEFACT_BUCKET_REGION }} + AWS_EC2_METADATA_DISABLED: true steps: - name: Checkout uses: actions/checkout@v4 @@ -19,8 +27,92 @@ jobs: with: go-version-file: go.mod - - name: Run full unit test suite - run: go test ./... + - name: Ensure tooling is available + run: | + set -euo pipefail + + if ! command -v aws >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y awscli + fi + + - name: Run full unit test suite with coverage + id: coverage + run: | + set -euo pipefail + + go test -covermode=atomic -coverprofile=coverage.out ./... + go tool cover -html=coverage.out -o coverage.html + + total="$(go tool cover -func=coverage.out | awk '/^total:/ {sub(/%/, "", $3); print $3}')" + printf '{\n "total": "%s"\n}\n' "$total" > coverage-summary.json + printf 'total=%s\n' "$total" >> "$GITHUB_OUTPUT" + + - name: Generate coverage badge + env: + COVERAGE_TOTAL: ${{ steps.coverage.outputs.total }} + run: | + set -euo pipefail + + color="$(awk -v total="$COVERAGE_TOTAL" 'BEGIN { + if (total >= 80) print "brightgreen"; + else if (total >= 70) print "green"; + else if (total >= 60) print "yellowgreen"; + else if (total >= 50) print "yellow"; + else print "red"; + }')" + + cat > coverage-badge.svg < + + + + + + + + + + + + + + coverage + coverage + ${COVERAGE_TOTAL}% + ${COVERAGE_TOTAL}% + + + EOF + + - name: Upload branch coverage artefacts + id: upload + run: | + set -euo pipefail + + aws configure set default.s3.addressing_style path + + repo_name="${GITHUB_REPOSITORY##*/}" + prefix="${repo_name}/branch/${GITHUB_REF_NAME}" + report_url="${ARTEFACT_BUCKET_ENDPONT%/}/${ARTEFACT_BUCKET_NAME}/${prefix}/coverage.html" + badge_url="${ARTEFACT_BUCKET_ENDPONT%/}/${ARTEFACT_BUCKET_NAME}/${prefix}/coverage-badge.svg" + + aws --endpoint-url "${ARTEFACT_BUCKET_ENDPONT}" s3 cp coverage.html "s3://${ARTEFACT_BUCKET_NAME}/${prefix}/coverage.html" --content-type text/html + aws --endpoint-url "${ARTEFACT_BUCKET_ENDPONT}" s3 cp coverage-badge.svg "s3://${ARTEFACT_BUCKET_NAME}/${prefix}/coverage-badge.svg" --content-type image/svg+xml + aws --endpoint-url "${ARTEFACT_BUCKET_ENDPONT}" s3 cp coverage-summary.json "s3://${ARTEFACT_BUCKET_NAME}/${prefix}/coverage-summary.json" --content-type application/json + + printf 'report_url=%s\n' "$report_url" >> "$GITHUB_OUTPUT" + printf 'badge_url=%s\n' "$badge_url" >> "$GITHUB_OUTPUT" + + - name: Add coverage summary + run: | + { + echo '## Coverage' + echo + echo '- Total: `${{ steps.coverage.outputs.total }}%`' + echo '- Report: ${{ steps.upload.outputs.report_url }}' + echo '- Badge: ${{ steps.upload.outputs.badge_url }}' + } >> "$GITHUB_STEP_SUMMARY" - name: Run behavior suite on main pushes if: ${{ github.ref == 'refs/heads/main' }}