Skip to content

feat: support /api/v1/artifacts/:reference API #19

feat: support /api/v1/artifacts/:reference API

feat: support /api/v1/artifacts/:reference API #19

Workflow file for this run

name: Code Coverage
on:
push:
branches: [main, release-*]
paths-ignore: ['**.md', '**.png', '**.jpg', '**.svg', '**/docs/**']
pull_request:
branches: [main, release-*]
paths-ignore: ['**.md', '**.png', '**.jpg', '**.svg', '**/docs/**']
# Only read-only permissions needed here.
# PR comments are posted by coverage-comment.yml via workflow_run
# so that fork PRs can also receive comments with write access.
permissions:
contents: read
env:
THRESHOLD_TOTAL: 70 # overall coverage threshold (%)
THRESHOLD_DIFF: 90 # changed-line coverage threshold (%)
jobs:
coverage:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # full history needed for diff coverage
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.24.2'
- name: Cache Go modules
uses: actions/cache@v4
with:
path: |
~/.cache/go-build
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
# ── Run tests ────────────────────────────────────────────────────────────
- name: Run tests with coverage (excluding pkg/server)
run: make test-coverage-ci
# ── Convert coverage format for diff-cover ────────────────────────────
- name: Install gocover-cobertura
run: go install github.com/t-yuki/gocover-cobertura@latest
- name: Convert to Cobertura XML
run: gocover-cobertura < coverage.out > coverage.xml
- name: Install diff-cover
run: pip install diff-cover
# ── Check total coverage threshold ───────────────────────────────────
- name: Check total coverage (>= ${{ env.THRESHOLD_TOTAL }}%)
id: total_coverage
run: |
TOTAL=$(go tool cover -func=coverage.out | tail -1 | awk '{print $3}' | sed 's/%//')
echo "total=${TOTAL}" >> "$GITHUB_OUTPUT"
echo "Total coverage: ${TOTAL}% (threshold: ${{ env.THRESHOLD_TOTAL }}%)"
if awk "BEGIN { exit !( ${TOTAL} + 0 < ${{ env.THRESHOLD_TOTAL }}) }"; then
echo "status=fail" >> "$GITHUB_OUTPUT"
echo "❌ Total coverage ${TOTAL}% is below the required ${{ env.THRESHOLD_TOTAL }}%"
else
echo "status=pass" >> "$GITHUB_OUTPUT"
echo "✅ Total coverage ${TOTAL}% meets the threshold of ${{ env.THRESHOLD_TOTAL }}%"
fi
# ── Check diff (changed-line) coverage threshold ─────────────────────
- name: Fetch base branch for diff
if: github.event_name == 'pull_request'
run: git fetch origin ${{ github.base_ref }} --depth=1
- name: Check diff coverage (>= ${{ env.THRESHOLD_DIFF }}%)
id: diff_coverage
if: github.event_name == 'pull_request'
run: |
BASE=origin/${{ github.base_ref }}
DIFF_OUTPUT=$(diff-cover coverage.xml \
--compare-branch="${BASE}" \
--format json:diff-cover.json \
--fail-under=${{ env.THRESHOLD_DIFF }} \
2>&1) && DIFF_EXIT=0 || DIFF_EXIT=$?
if [ -f diff-cover.json ]; then
DIFF_PCT=$(python3 - <<'PY'
import json

Check failure on line 93 in .github/workflows/coverage.yml

View workflow run for this annotation

GitHub Actions / .github/workflows/coverage.yml

Invalid workflow file

You have an error in your yaml syntax on line 93
with open('diff-cover.json', encoding='utf-8') as f:
report = json.load(f)
num_changed_lines = report.get('num_changed_lines', 0)
total_num_lines = report.get('total_num_lines', 0)
total_percent_covered = report.get('total_percent_covered', 'N/A')
if num_changed_lines > 0 and total_num_lines == 0:
print('N/A')
else:
print(total_percent_covered)
PY
)
else
DIFF_PCT="N/A"
fi
if [ "$DIFF_PCT" = "N/A" ]; then
DIFF_DISPLAY="N/A"
else
DIFF_DISPLAY="${DIFF_PCT}%"
fi
echo "diff_pct=${DIFF_PCT}" >> "$GITHUB_OUTPUT"
echo "diff_display=${DIFF_DISPLAY}" >> "$GITHUB_OUTPUT"
echo "$DIFF_OUTPUT"
if [ "$DIFF_EXIT" -ne 0 ] || [ "$DIFF_PCT" = "N/A" ]; then
echo "diff_status=fail" >> "$GITHUB_OUTPUT"
if [ "$DIFF_PCT" = "N/A" ]; then
echo "❌ Changed-line coverage could not be computed because diff-cover found changed lines without matching coverage information."
fi
else
echo "diff_status=pass" >> "$GITHUB_OUTPUT"
fi
# ── Generate per-package coverage table ──────────────────────────────
- name: Generate coverage report markdown
if: always()
run: |
{
echo "## 📊 Code Coverage Report"
echo ""
TOTAL="${{ steps.total_coverage.outputs.total }}"
STATUS="${{ steps.total_coverage.outputs.status }}"
[ -n "$TOTAL" ] || TOTAL="N/A"
[ "$STATUS" = "pass" ] && ICON="✅" || ICON="❌"
echo "| Metric | Coverage | Threshold | Status |"
echo "|--------|----------|-----------|--------|"
echo "| Overall | **${TOTAL}%** | ${{ env.THRESHOLD_TOTAL }}% | ${ICON} |"
if [ "${{ github.event_name }}" = "pull_request" ]; then
DIFF_DISPLAY="${{ steps.diff_coverage.outputs.diff_display }}"
DIFF_STATUS="${{ steps.diff_coverage.outputs.diff_status }}"
[ -n "$DIFF_DISPLAY" ] || DIFF_DISPLAY="N/A"
[ "$DIFF_STATUS" = "pass" ] && DIFF_ICON="✅" || DIFF_ICON="❌"
echo "| Changed lines | **${DIFF_DISPLAY}** | ${{ env.THRESHOLD_DIFF }}% | ${DIFF_ICON} |"
fi
if [ -f coverage.out ]; then
echo ""
echo "<details>"
echo "<summary>📦 Per-package breakdown</summary>"
echo ""
echo '```'
go tool cover -func=coverage.out | grep -v "^total:" | \
awk '{printf "%-80s %s\n", $1, $3}' | sort
echo ""
go tool cover -func=coverage.out | grep "^total:"
echo '```'
echo ""
echo "</details>"
else
echo ""
echo "Coverage artifacts were not generated because the workflow failed before coverage collection completed."
fi
} > coverage-report.md
# ── Write step summary ────────────────────────────────────────────────
- name: Write job summary
if: always()
run: cat coverage-report.md >> "$GITHUB_STEP_SUMMARY"
# ── Save PR number so the comment workflow can find the right PR ──────
- name: Save PR number
if: always() && github.event_name == 'pull_request'
run: echo "${{ github.event.pull_request.number }}" > pr-number.txt
# ── Upload artifacts (report + pr-number for comment workflow) ────────
- name: Upload coverage artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: |
coverage.out
coverage.xml
diff-cover.json
coverage-report.md
pr-number.txt
if-no-files-found: warn
retention-days: 14
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
file: ./coverage.out
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
# ── Enforce thresholds (after artifacts uploaded so report is always saved) ──
- name: Enforce total coverage threshold
if: steps.total_coverage.outputs.status == 'fail'
run: |
echo "❌ Total coverage ${{ steps.total_coverage.outputs.total }}% is below the required ${{ env.THRESHOLD_TOTAL }}%"
exit 1
- name: Enforce diff coverage threshold
if: >
github.event_name == 'pull_request' &&
steps.diff_coverage.outputs.diff_status == 'fail'
run: |
echo "❌ Changed-line coverage ${{ steps.diff_coverage.outputs.diff_display }} is below the required ${{ env.THRESHOLD_DIFF }}%"
exit 1