44 pull_request :
55 branches :
66 - ' **'
7+ push :
8+ branches :
9+ - main
710
811permissions :
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
1221jobs :
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}% 
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"
0 commit comments