Skip to content

Commit 6714695

Browse files
Merge pull request #27 from DataDog/feat/datadog-ci-integration
feat(ci): integrate Datadog CI products for enhanced visibility
2 parents 2fa0dc3 + b9412d2 commit 6714695

4 files changed

Lines changed: 137 additions & 108 deletions

File tree

.datadog-ci.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"apiKey": "${DATADOG_API_KEY}",
3+
"appKey": "${DD_APP_KEY}",
4+
"site": "${DD_SITE}",
5+
"files": [
6+
"coverage.out"
7+
],
8+
"coverage": {
9+
"format": "go-cover",
10+
"basePath": "."
11+
},
12+
"sast": {
13+
"enabled": true,
14+
"languages": ["go"],
15+
"rules": "recommended"
16+
}
17+
}

.github/workflows/ci.yml

Lines changed: 101 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,92 @@ on:
44
pull_request:
55
branches:
66
- '**'
7+
push:
8+
branches:
9+
- main
710

811
permissions:
912
contents: write # Needed to commit coverage badge on main branch
1013
pull-requests: write
14+
security-events: write # Needed for Datadog SAST results
15+
id-token: write # Needed to federate STS token to upload coverage
16+
17+
env:
18+
DD_ENV: ci
19+
DD_SERVICE: pup
1120

1221
jobs:
1322
test:
1423
name: Test and Coverage
1524
runs-on: ubuntu-latest
25+
env:
26+
DD_API_KEY: ${{ secrets.DD_API_KEY }}
27+
DD_SITE: ${{ secrets.DD_SITE || 'datadoghq.com' }}
28+
DD_CIVISIBILITY_AGENTLESS_ENABLED: true
29+
DD_CIVISIBILITY_GIT_UPLOAD_ENABLED: true
30+
DD_CIVISIBILITY_ENABLED: true
1631
steps:
1732
- name: Checkout code
1833
uses: actions/checkout@v6
34+
with:
35+
fetch-depth: 0 # Fetch full git history for Datadog CI Visibility
1936

2037
- name: Set up Go
2138
uses: actions/setup-go@v6
2239
with:
2340
go-version: '1.25'
2441
cache: true
2542

43+
- name: Get Datadog credentials
44+
id: dd-sts # Needed to be able to reference this step's output later
45+
uses: DataDog/dd-sts-action@main # Pin to the main branch to get auto updates for free, or to a specific commit hash if you want stability
46+
with:
47+
policy: public-datadog-pup-sts
48+
49+
- name: Configure Datadog Test Optimization
50+
uses: datadog/test-visibility-github-action@v2
51+
with:
52+
languages: go
53+
api_key: ${{ steps.dd-sts.outputs.api_key }}
54+
site: datadoghq.com
55+
56+
- name: Install Datadog CI tools
57+
run: |
58+
# Install orchestrion for Go test instrumentation
59+
go install github.com/DataDog/orchestrion@latest
60+
echo "$(go env GOPATH)/bin" >> $GITHUB_PATH
61+
62+
# Install datadog-ci CLI for coverage upload
63+
npm install -g @datadog/datadog-ci
64+
2665
- name: Install bc for floating-point math
2766
run: sudo apt-get update && sudo apt-get install -y bc
2867

2968
- name: Run tests with coverage
69+
env:
70+
DD_CIVISIBILITY_ENABLED: true
71+
DD_ENV: ci
3072
run: |
31-
# Run tests on all packages with race detection and parallel execution
32-
go test -v -race -parallel 8 ./...
33-
# Calculate coverage only for pkg/ (cmd/ is CLI code with lower test coverage)
34-
# Use -count=1 to disable test caching and get accurate coverage
35-
# Use -parallel 8 for faster execution (4.7x faster than sequential)
36-
go test -count=1 -parallel 8 -coverprofile=coverage.out -covermode=atomic ./pkg/...
73+
# Run tests on all packages with race detection
74+
# Use orchestrion to instrument tests for Datadog CI Visibility
75+
# IMPORTANT: Use -parallel 256 with orchestrion. The test-visibility-github-action
76+
# sets GOFLAGS='-toolexec=orchestrion toolexec' which auto-injects t.Parallel()
77+
# into ALL subtests. With the default parallel limit (GOMAXPROCS=2 on runners),
78+
# this deadlocks table-driven tests where parent tests wait for subtests that are
79+
# blocked waiting for parallel slots. A high limit avoids the deadlock.
80+
if [ -n "$DD_API_KEY" ]; then
81+
echo "Running tests with Datadog CI Visibility enabled"
82+
orchestrion go test -v -race -parallel 256 ./...
83+
# Calculate coverage with orchestrion instrumentation
84+
# Use -count=1 to disable test caching and get accurate coverage
85+
orchestrion go test -count=1 -parallel 256 -coverprofile=coverage.out -covermode=atomic ./pkg/...
86+
else
87+
echo "Running tests without Datadog CI Visibility (DD_API_KEY not set)"
88+
go test -v -race ./...
89+
go test -count=1 -coverprofile=coverage.out -covermode=atomic ./pkg/...
90+
fi
91+
92+
# Generate HTML coverage report
3793
go tool cover -html=coverage.out -o coverage.html
3894
3995
- name: Calculate coverage
@@ -57,6 +113,15 @@ jobs:
57113
echo "" >> coverage_report.txt
58114
go tool cover -func=coverage.out | tail -1 >> coverage_report.txt
59115
116+
- name: Upload coverage to Datadog
117+
if: env.DD_API_KEY != ''
118+
env:
119+
DATADOG_API_KEY: ${{ steps.dd-sts.outputs.api_key }}
120+
DD_SITE: datadoghq.com
121+
run: |
122+
# Upload coverage reports to Datadog
123+
datadog-ci coverage upload --format=go-coverprofile coverage.out
124+
60125
- name: Check coverage threshold
61126
run: |
62127
COVERAGE=${{ steps.coverage.outputs.coverage }}
@@ -75,107 +140,6 @@ jobs:
75140
echo "✅ Coverage $COVERAGE% meets threshold $THRESHOLD%"
76141
fi
77142
78-
- name: Generate coverage badge data
79-
if: github.event_name == 'pull_request'
80-
id: badge
81-
run: |
82-
COVERAGE=${{ steps.coverage.outputs.coverage }}
83-
84-
# Determine badge color based on coverage
85-
if [ $(echo "$COVERAGE >= 90" | bc -l) -eq 1 ]; then
86-
COLOR="brightgreen"
87-
elif [ $(echo "$COVERAGE >= 80" | bc -l) -eq 1 ]; then
88-
COLOR="green"
89-
elif [ $(echo "$COVERAGE >= 70" | bc -l) -eq 1 ]; then
90-
COLOR="yellow"
91-
elif [ $(echo "$COVERAGE >= 60" | bc -l) -eq 1 ]; then
92-
COLOR="orange"
93-
else
94-
COLOR="red"
95-
fi
96-
97-
echo "color=$COLOR" >> $GITHUB_OUTPUT
98-
99-
- name: Generate PR comment body
100-
if: github.event_name == 'pull_request'
101-
id: comment
102-
env:
103-
COVERAGE: ${{ steps.coverage.outputs.coverage }}
104-
BADGE_COLOR: ${{ steps.badge.outputs.color }}
105-
COMMIT_SHA: ${{ github.event.pull_request.head.sha }}
106-
run: |
107-
# Determine status
108-
if [ $(echo "$COVERAGE >= 80" | bc -l) -eq 1 ]; then
109-
STATUS="✅ PASSED - Coverage meets minimum threshold"
110-
STATUS_EMOJI="✅"
111-
else
112-
STATUS="❌ FAILED - Coverage below minimum threshold"
113-
STATUS_EMOJI="❌"
114-
fi
115-
116-
# Create comment body using heredoc
117-
cat > comment_final.txt << EOF
118-
## 📊 Test Coverage Report
119-
120-
**Overall Coverage:** ${COVERAGE}% ![Coverage](https://img.shields.io/badge/coverage-${COVERAGE}%25-${BADGE_COLOR})
121-
122-
**Threshold:** 80% ${STATUS_EMOJI}
123-
124-
<details>
125-
<summary>Coverage by Package</summary>
126-
127-
\`\`\`
128-
$(cat coverage_report.txt)
129-
\`\`\`
130-
131-
</details>
132-
133-
---
134-
📈 **Coverage Status:** ${STATUS}
135-
136-
<sub>Updated for commit ${COMMIT_SHA}</sub>
137-
EOF
138-
139-
- name: Comment on PR
140-
if: github.event_name == 'pull_request'
141-
continue-on-error: true # Don't fail CI if comment posting fails
142-
uses: actions/github-script@v8
143-
env:
144-
COMMENT_BODY: ${{ steps.comment.outputs.comment_body }}
145-
with:
146-
script: |
147-
const fs = require('fs');
148-
const commentBody = fs.readFileSync('comment_final.txt', 'utf8');
149-
150-
// Find existing comment
151-
const { data: comments } = await github.rest.issues.listComments({
152-
owner: context.repo.owner,
153-
repo: context.repo.repo,
154-
issue_number: context.issue.number,
155-
});
156-
157-
const botComment = comments.find(comment =>
158-
comment.user.type === 'Bot' && comment.body.includes('📊 Test Coverage Report')
159-
);
160-
161-
if (botComment) {
162-
// Update existing comment
163-
await github.rest.issues.updateComment({
164-
owner: context.repo.owner,
165-
repo: context.repo.repo,
166-
comment_id: botComment.id,
167-
body: commentBody
168-
});
169-
} else {
170-
// Create new comment
171-
await github.rest.issues.createComment({
172-
owner: context.repo.owner,
173-
repo: context.repo.repo,
174-
issue_number: context.issue.number,
175-
body: commentBody
176-
});
177-
}
178-
179143
- name: Generate coverage badge for main branch
180144
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
181145
env:
@@ -262,3 +226,33 @@ jobs:
262226

263227
- name: Verify binary
264228
run: ./pup --version
229+
230+
sast:
231+
name: Datadog Static Analysis
232+
runs-on: ubuntu-latest
233+
if: github.event_name == 'pull_request'
234+
env:
235+
DD_API_KEY: ${{ secrets.DD_API_KEY }}
236+
DD_APP_KEY: ${{ secrets.DD_APP_KEY }}
237+
DD_SITE: ${{ secrets.DD_SITE || 'datadoghq.com' }}
238+
steps:
239+
- name: Checkout code
240+
uses: actions/checkout@v6
241+
with:
242+
fetch-depth: 0 # Full history for better SAST analysis
243+
244+
- name: Run Datadog Static Analysis
245+
if: env.DD_API_KEY != ''
246+
run: |
247+
# Install datadog-ci if not already installed
248+
npm install -g @datadog/datadog-ci
249+
250+
# Run static analysis
251+
# This will analyze the code for security vulnerabilities, code quality issues, etc.
252+
datadog-ci sast scan --service=${{ env.DD_SERVICE }} --env=${{ env.DD_ENV }}
253+
254+
- name: SAST disabled notice
255+
if: env.DD_API_KEY == ''
256+
run: |
257+
echo "⚠️ Datadog Static Analysis skipped: DD_API_KEY not configured"
258+
echo "To enable SAST, add DD_API_KEY and DD_APP_KEY as repository secrets"

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Go-based CLI wrapper for Datadog APIs. Provides OAuth2 + API key authentication
77
- **[COMMANDS.md](docs/COMMANDS.md)** - Complete command reference with all 33 domains
88
- **[CONTRIBUTING.md](docs/CONTRIBUTING.md)** - Git workflow, PR process, commit format
99
- **[TESTING.md](docs/TESTING.md)** - Test strategy, coverage requirements, CI/CD
10+
- **[DATADOG_CI.md](docs/DATADOG_CI.md)** - Datadog CI products integration (Test Visibility, Code Coverage, SAST)
1011
- **[OAUTH2.md](docs/OAUTH2.md)** - OAuth2 implementation details (DCR, PKCE, token storage)
1112
- **[EXAMPLES.md](docs/EXAMPLES.md)** - Usage examples and common workflows
1213
- **[ARCHITECTURE.md](docs/ARCHITECTURE.md)** - Design decisions and technical details

docs/TESTING.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,9 @@ func TestMetricsCommands(t *testing.T) {
168168

169169
## CI/CD Pipeline
170170

171-
GitHub Actions workflow runs on all branches with 3 parallel jobs:
171+
> **Note:** Pup uses Datadog CI products for enhanced monitoring and analytics.
172+
173+
GitHub Actions workflow runs on all branches with 4 parallel jobs:
172174

173175
### 1. Test and Coverage
174176

@@ -230,6 +232,21 @@ Enforces Go style and best practices.
230232

231233
Verifies project builds and binary executes.
232234

235+
### 4. Datadog Static Analysis (SAST)
236+
237+
```yaml
238+
- name: Run Datadog Static Analysis
239+
run: datadog-ci sast scan --service=pup --env=ci
240+
```
241+
242+
Scans code for security vulnerabilities and quality issues on pull requests.
243+
244+
See **[DATADOG_CI.md](DATADOG_CI.md)** for:
245+
- Test Visibility with orchestrion
246+
- Code Coverage upload to Datadog
247+
- CI Pipeline Visibility tracking
248+
- SAST configuration and results
249+
233250
## Coverage Badge
234251

235252
README.md displays live coverage badge:

0 commit comments

Comments
 (0)