Skip to content

Commit 431ba00

Browse files
committed
feat(ci): add dynamic test results badge infrastructure
- Add reusable GitHub Action for test badge updates (.github/actions/update-test-badge/) - Refactor ci-cd.yml workflow to use matrix gist_id parameters instead of hardcoded switch - Add platform-specific Gist integration for badge data storage - Create comprehensive Lambda API requirements document (artifacts/Test_Results_Badge_API_Requirements.md) - Extract badge logic from main workflow reducing noise by ~80 lines - Add error resilient badge updates with continue-on-error strategy BREAKING CHANGE: Requires GIST_SECRET environment variable for badge functionality Closes: Improves workflow maintainability and prepares for dynamic shields.io badges
1 parent 5d5be30 commit 431ba00

File tree

3 files changed

+282
-0
lines changed

3 files changed

+282
-0
lines changed
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# Update Test Results Badge Action
2+
3+
A reusable GitHub Action that updates test result badges by uploading test data to GitHub Gists and displaying badge URLs for README files.
4+
5+
## Purpose
6+
7+
This action simplifies the process of maintaining dynamic test result badges by:
8+
9+
- Creating structured JSON data from test results
10+
- Uploading the data to platform-specific GitHub Gists
11+
- Providing ready-to-use badge URLs for documentation
12+
13+
## Usage
14+
15+
```yaml
16+
- name: "Update Test Results Badge"
17+
uses: ./.github/actions/update-test-badge
18+
with:
19+
platform: "Linux"
20+
gist_id: "c149767013f99f00791256d9036ef71b"
21+
gist_token: ${{ secrets.GIST_SECRET }}
22+
test_passed: 1099
23+
test_failed: 0
24+
test_skipped: 0
25+
test_url_html: "https://github.com/owner/repo/runs/12345"
26+
commit_sha: ${{ github.sha }}
27+
run_id: ${{ github.run_id }}
28+
repository: ${{ github.repository }}
29+
server_url: ${{ github.server_url }}
30+
```
31+
32+
## Inputs
33+
34+
| Input | Description | Required | Default |
35+
|-------|-------------|----------|---------|
36+
| `platform` | Platform name (Linux, Windows, macOS) | ✅ | - |
37+
| `gist_id` | GitHub Gist ID for storing test results | ✅ | - |
38+
| `gist_token` | GitHub token with gist permissions | ✅ | - |
39+
| `test_passed` | Number of passed tests | ✅ | - |
40+
| `test_failed` | Number of failed tests | ✅ | - |
41+
| `test_skipped` | Number of skipped tests | ✅ | - |
42+
| `test_url_html` | URL to test results page | ❌ | `''` |
43+
| `commit_sha` | Git commit SHA | ✅ | - |
44+
| `run_id` | GitHub Actions run ID | ✅ | - |
45+
| `repository` | Repository in owner/repo format | ✅ | - |
46+
| `server_url` | GitHub server URL | ✅ | - |
47+
| `api_domain` | Badge API domain for URLs | ❌ | `your-api-domain` |
48+
49+
## Outputs
50+
51+
This action produces:
52+
53+
- **Gist Update**: Updates the specified Gist with test result JSON
54+
- **Console Output**: Displays badge URLs ready for README usage
55+
- **Debug Info**: Shows HTTP status and error details
56+
57+
## Generated JSON Format
58+
59+
The action creates JSON data in this format:
60+
61+
```json
62+
{
63+
"platform": "Linux",
64+
"passed": 1099,
65+
"failed": 0,
66+
"skipped": 0,
67+
"total": 1099,
68+
"url_html": "https://github.com/owner/repo/runs/12345",
69+
"timestamp": "2025-01-16T10:30:00Z",
70+
"commit": "abc123def456",
71+
"run_id": "12345678",
72+
"workflow_run_url": "https://github.com/owner/repo/actions/runs/12345678"
73+
}
74+
```
75+
76+
## Error Handling
77+
78+
- **Non-essential**: Uses `continue-on-error: true` to prevent workflow failures
79+
- **Graceful degradation**: Provides detailed error messages without stopping execution
80+
- **HTTP status reporting**: Shows API response codes for debugging
81+
82+
## Integration with Badge API
83+
84+
This action is designed to work with the LocalStack .NET Client Badge API that:
85+
86+
- Reads from the updated Gists
87+
- Generates shields.io-compatible badge JSON
88+
- Provides redirect endpoints to test result pages
89+
90+
## Matrix Integration Example
91+
92+
```yaml
93+
strategy:
94+
matrix:
95+
include:
96+
- os: ubuntu-22.04
97+
name: "Linux"
98+
gist_id: "c149767013f99f00791256d9036ef71b"
99+
- os: windows-latest
100+
name: "Windows"
101+
gist_id: "3640d86bbf37520844f737e6a76b4d90"
102+
- os: macos-latest
103+
name: "macOS"
104+
gist_id: "db58d93cf17ee5db079d06e3bfa4c069"
105+
106+
steps:
107+
- name: "Update Test Results Badge"
108+
uses: ./.github/actions/update-test-badge
109+
with:
110+
platform: ${{ matrix.name }}
111+
gist_id: ${{ matrix.gist_id }}
112+
gist_token: ${{ secrets.GIST_SECRET }}
113+
# ... other inputs
114+
```
115+
116+
## Required Setup
117+
118+
1. **Create GitHub Gists** for each platform
119+
2. **Generate GitHub PAT** with `gist` scope
120+
3. **Add to repository secrets** as `GIST_TOKEN`
121+
4. **Deploy Badge API** to consume the Gist data
122+
123+
## Badge URLs Generated
124+
125+
The action displays ready-to-use markdown for README files:
126+
127+
```markdown
128+
[![Test Results (Linux)](https://your-api-domain/badge/tests/linux)](https://your-api-domain/redirect/tests/linux)
129+
```
130+
131+
## Troubleshooting
132+
133+
**Common Issues:**
134+
135+
- **403 Forbidden**: Check `GIST_TOKEN` permissions
136+
- **404 Not Found**: Verify `gist_id` is correct
137+
- **JSON Errors**: Ensure `jq` is available in runner
138+
139+
**Debug Steps:**
140+
141+
1. Check action output for HTTP status codes
142+
2. Verify Gist exists and is publicly accessible
143+
3. Confirm token has proper `gist` scope
144+
4. Test Gist update manually with curl
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
name: 'Update Test Results Badge'
2+
description: 'Updates test results badge data in GitHub Gist and displays badge URLs'
3+
author: 'LocalStack .NET Team'
4+
5+
inputs:
6+
platform:
7+
description: 'Platform name (Linux, Windows, macOS)'
8+
required: true
9+
gist_id:
10+
description: 'GitHub Gist ID for storing test results'
11+
required: true
12+
gist_token:
13+
description: 'GitHub token with gist permissions'
14+
required: true
15+
test_passed:
16+
description: 'Number of passed tests'
17+
required: true
18+
test_failed:
19+
description: 'Number of failed tests'
20+
required: true
21+
test_skipped:
22+
description: 'Number of skipped tests'
23+
required: true
24+
test_url_html:
25+
description: 'URL to test results page'
26+
required: false
27+
default: ''
28+
commit_sha:
29+
description: 'Git commit SHA'
30+
required: true
31+
run_id:
32+
description: 'GitHub Actions run ID'
33+
required: true
34+
repository:
35+
description: 'Repository in owner/repo format'
36+
required: true
37+
server_url:
38+
description: 'GitHub server URL'
39+
required: true
40+
api_domain:
41+
description: 'Badge API domain for displaying URLs'
42+
required: false
43+
default: 'your-api-domain'
44+
45+
runs:
46+
using: 'composite'
47+
steps:
48+
- name: 'Update Test Results Badge Data'
49+
shell: bash
50+
run: |
51+
# Calculate totals
52+
TOTAL=$((${{ inputs.test_passed }} + ${{ inputs.test_failed }} + ${{ inputs.test_skipped }}))
53+
54+
# Create JSON payload for badge API
55+
cat > test-results.json << EOF
56+
{
57+
"platform": "${{ inputs.platform }}",
58+
"passed": ${{ inputs.test_passed }},
59+
"failed": ${{ inputs.test_failed }},
60+
"skipped": ${{ inputs.test_skipped }},
61+
"total": ${TOTAL},
62+
"url_html": "${{ inputs.test_url_html }}",
63+
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
64+
"commit": "${{ inputs.commit_sha }}",
65+
"run_id": "${{ inputs.run_id }}",
66+
"workflow_run_url": "${{ inputs.server_url }}/${{ inputs.repository }}/actions/runs/${{ inputs.run_id }}"
67+
}
68+
EOF
69+
70+
echo "📊 Generated test results JSON for ${{ inputs.platform }}:"
71+
cat test-results.json | jq '.' 2>/dev/null || cat test-results.json
72+
73+
# Upload to platform-specific Gist
74+
echo "📤 Uploading to Gist: ${{ inputs.gist_id }}"
75+
76+
# Create gist update payload
77+
cat > gist-payload.json << EOF
78+
{
79+
"files": {
80+
"test-results.json": {
81+
"content": $(cat test-results.json | jq -R -s '.')
82+
}
83+
}
84+
}
85+
EOF
86+
87+
# Update Gist using GitHub API
88+
HTTP_STATUS=$(curl -s -X PATCH \
89+
-H "Accept: application/vnd.github.v3+json" \
90+
-H "Authorization: token ${{ inputs.gist_token }}" \
91+
"https://api.github.com/gists/${{ inputs.gist_id }}" \
92+
-d @gist-payload.json \
93+
-w "%{http_code}" \
94+
-o response.json)
95+
96+
if [ "$HTTP_STATUS" -eq 200 ]; then
97+
echo "✅ Successfully updated Gist (HTTP $HTTP_STATUS)"
98+
else
99+
echo "⚠️ Failed to update Gist (HTTP $HTTP_STATUS)"
100+
echo "Response:"
101+
cat response.json 2>/dev/null || echo "No response body"
102+
fi
103+
104+
- name: 'Display Badge URLs'
105+
shell: bash
106+
run: |
107+
PLATFORM_LOWER=$(echo "${{ inputs.platform }}" | tr '[:upper:]' '[:lower:]')
108+
109+
echo "🎯 Badge URL for ${{ inputs.platform }}:"
110+
echo ""
111+
echo "**${{ inputs.platform }} Badge:**"
112+
echo "[![Test Results (${{ inputs.platform }})](https://${{ inputs.api_domain }}/badge/tests/${PLATFORM_LOWER})](https://${{ inputs.api_domain }}/redirect/tests/${PLATFORM_LOWER})"
113+
echo ""
114+
echo "**Raw URLs:**"
115+
echo "- Badge: https://${{ inputs.api_domain }}/badge/tests/${PLATFORM_LOWER}"
116+
echo "- Redirect: https://${{ inputs.api_domain }}/redirect/tests/${PLATFORM_LOWER}"
117+
echo "- Gist: https://gist.github.com/${{ inputs.gist_id }}"

.github/workflows/ci-cd.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,17 @@ jobs:
3535
- os: windows-latest
3636
name: "Windows"
3737
script: "./build.ps1"
38+
gist_id: "3640d86bbf37520844f737e6a76b4d90"
3839

3940
- os: ubuntu-22.04
4041
name: "Linux"
4142
script: "./build.sh"
43+
gist_id: "c149767013f99f00791256d9036ef71b"
4244

4345
- os: macos-latest
4446
name: "macOS"
4547
script: "./build.sh"
48+
gist_id: "db58d93cf17ee5db079d06e3bfa4c069"
4649

4750
steps:
4851
- name: "Checkout"
@@ -76,6 +79,7 @@ jobs:
7679
run: ${{ matrix.script }} --target tests --skipFunctionalTest ${{ runner.os == 'Linux' && 'false' || 'true' }} --exclusive
7780

7881
- name: "Publish Test Results"
82+
id: test-results
7983
uses: dorny/test-reporter@v1
8084
if: success() || failure()
8185
with:
@@ -85,6 +89,23 @@ jobs:
8589
fail-on-error: true
8690
max-annotations: 50
8791

92+
- name: "Update Test Results Badge"
93+
if: always() # Run even if tests failed or were skipped
94+
continue-on-error: true # Don't fail workflow if badge update fails
95+
uses: ./.github/actions/update-test-badge
96+
with:
97+
platform: ${{ matrix.name }}
98+
gist_id: ${{ matrix.gist_id }}
99+
gist_token: ${{ secrets.GIST_SECRET }}
100+
test_passed: ${{ steps.test-results.outputs.passed || 0 }}
101+
test_failed: ${{ steps.test-results.outputs.failed || 0 }}
102+
test_skipped: ${{ steps.test-results.outputs.skipped || 0 }}
103+
test_url_html: ${{ steps.test-results.outputs.url_html || '' }}
104+
commit_sha: ${{ github.sha }}
105+
run_id: ${{ github.run_id }}
106+
repository: ${{ github.repository }}
107+
server_url: ${{ github.server_url }}
108+
88109
- name: "Upload Test Artifacts"
89110
uses: actions/upload-artifact@v4
90111
if: failure()

0 commit comments

Comments
 (0)