Skip to content

Commit 40d7217

Browse files
authored
Automated Perfetto UI Notifications (#3255)
* Testing visualization generation * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Adding dummy test data * Update Jenkinsfile * Update Jenkinsfile * Adding notifications * Testing * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Image compression * Update Jenkinsfile * Moving capture logic to main Jenkins file * Testing generation * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Fixing curl request * Update Jenkinsfile * Clean up * Fix * Fixing notification * Testing message creation * Adjusting message payload * Testing notification generation * Updating main jenkinsfile * Fixing cleanup call * Removing test pipeline code * Comment clean up * Testing pipeline * Update Jenkinsfile * Update Jenkinsfile * Update Jenkinsfile * Moving archive Moving trace archive to safe location before source checkout * Removing test pipeline * Testing pipeline with unique file names * Update Jenkinsfile * Removing test files Updated main pipeline
1 parent de64664 commit 40d7217

2 files changed

Lines changed: 185 additions & 0 deletions

File tree

Jenkinsfile

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,129 @@ def sendFailureNotifications() {
7272
}
7373
}
7474

75+
def generateAndArchiveBuildTraceVisualization() {
76+
try {
77+
def buildTraceFileName = "ck_build_trace.json";
78+
79+
// Attempt to download the build trace file to check if it exists
80+
def traceFileExists = false
81+
try {
82+
copyArtifacts(
83+
projectName: env.JOB_NAME,
84+
selector: specific(env.BUILD_NUMBER),
85+
filter: buildTraceFileName
86+
)
87+
traceFileExists = fileExists(buildTraceFileName)
88+
} catch (Exception e) {
89+
echo "Could not copy artifacts: ${e.getMessage()}"
90+
traceFileExists = false
91+
}
92+
93+
sh """
94+
echo "post download:"
95+
ls -la
96+
"""
97+
98+
if (traceFileExists) {
99+
// Move the build trace file to a temporary location to preserve it during checkout
100+
sh """
101+
mkdir -p /tmp/jenkins_artifacts
102+
cp ${buildTraceFileName} /tmp/jenkins_artifacts/${buildTraceFileName}
103+
ls -la /tmp/jenkins_artifacts/
104+
"""
105+
} else {
106+
echo "Build trace archive not found"
107+
return
108+
}
109+
110+
// Checkout source code to get required files
111+
checkout scm
112+
113+
// Restore the build trace file after checkout
114+
sh """
115+
ls -la
116+
cp /tmp/jenkins_artifacts/${buildTraceFileName} ${buildTraceFileName}
117+
ls -la ${buildTraceFileName}
118+
"""
119+
120+
// Pull image
121+
def image = "ghcr.io/puppeteer/puppeteer:24.30.0"
122+
echo "Pulling image: ${image}"
123+
def retimage = docker.image("${image}")
124+
retimage.pull()
125+
126+
// Create a temporary workspace
127+
sh """#!/bin/bash
128+
ls -la
129+
mkdir -p workspace
130+
cp ./script/infra_helper/capture_build_trace.js ./workspace
131+
cp ${buildTraceFileName} ./workspace/${buildTraceFileName}
132+
chmod 777 ./workspace
133+
ls -la ./workspace
134+
"""
135+
136+
// Run container to get snapshot
137+
def dockerOpts = "--cap-add=SYS_ADMIN -v \"\$(pwd)/workspace:/workspace\" -e NODE_PATH=/home/pptruser/node_modules"
138+
// Create unique image name by sanitizing job name
139+
def sanitizedJobName = env.JOB_NAME.replaceAll(/[\/\\:*?"<>| ]/, '_')
140+
def imageName = "perfetto_snapshot_${sanitizedJobName}_build_${env.BUILD_NUMBER}.png"
141+
sh """
142+
docker run --rm ${dockerOpts} ${image} node /workspace/capture_build_trace.js
143+
mv ./workspace/perfetto_snapshot_build.png ./workspace/${imageName}
144+
"""
145+
146+
// Archive the snapshot
147+
sh """
148+
mv ./workspace/${imageName} ${imageName}
149+
"""
150+
archiveArtifacts "${imageName}"
151+
152+
// Notify the channel
153+
withCredentials([string(credentialsId: 'ck_ci_build_perf_webhook_url', variable: 'WEBHOOK_URL')]) {
154+
sh '''
155+
# Create build trace filename with build number based on the original filename
156+
BUILD_TRACE_WITH_NUMBER=$(echo "''' + buildTraceFileName + '''" | sed 's/.json/_''' + sanitizedJobName + '''_''' + env.BUILD_NUMBER + '''.json/')
157+
158+
# Convert image to base64
159+
echo "Converting image to base64..."
160+
IMAGE_BASE64=$(base64 -w 0 ''' + imageName + ''')
161+
echo "Image base64 length: ${#IMAGE_BASE64}"
162+
163+
# Convert build trace to base64
164+
echo "Converting build trace to base64..."
165+
BUILD_TRACE_BASE64=$(base64 -w 0 ''' + buildTraceFileName + ''')
166+
echo "Build trace base64 length: ${#BUILD_TRACE_BASE64}"
167+
168+
# Create JSON payload with base64 data
169+
echo "Creating JSON payload..."
170+
{
171+
printf '{\n'
172+
printf ' "jobName": "%s",\n' "''' + env.JOB_NAME + '''"
173+
printf ' "buildNumber": "%s",\n' "''' + env.BUILD_NUMBER + '''"
174+
printf ' "jobUrl": "%s",\n' "''' + env.RUN_DISPLAY_URL + '''"
175+
printf ' "imageName": "%s",\n' "''' + imageName + '''"
176+
printf ' "imageData": "%s",\n' "$IMAGE_BASE64"
177+
printf ' "buildTraceName": "%s",\n' "$BUILD_TRACE_WITH_NUMBER"
178+
printf ' "buildTraceData": "%s"\n' "$BUILD_TRACE_BASE64"
179+
printf '}\n'
180+
} > webhook_payload.json
181+
182+
echo "JSON payload created, size: $(wc -c < webhook_payload.json) bytes"
183+
184+
curl -X POST "${WEBHOOK_URL}" \
185+
-H "Content-Type: application/json" \
186+
-d @webhook_payload.json
187+
188+
# Clean up temporary file
189+
rm -f webhook_payload.json
190+
'''
191+
}
192+
} catch (Exception e) {
193+
echo "Throwing error exception while generating build trace visualization"
194+
echo 'Exception occurred: ' + e.toString()
195+
}
196+
}
197+
75198
class Version {
76199
int major, minor, patch
77200
@Override
@@ -1750,6 +1873,15 @@ pipeline {
17501873
}
17511874
}
17521875
post {
1876+
always {
1877+
node(rocmnode("nogpu")) {
1878+
script {
1879+
// Simulate capture
1880+
generateAndArchiveBuildTraceVisualization()
1881+
}
1882+
cleanWs()
1883+
}
1884+
}
17531885
success {
17541886
script {
17551887
// Report the parent stage build ck and run tests status
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
const puppeteer = require('puppeteer');
2+
3+
(async () => {
4+
try {
5+
// Launch the browser
6+
const browser = await puppeteer.launch({
7+
args: [
8+
'--no-sandbox',
9+
'--headless',
10+
'--disable-gpu',
11+
'--window-size=1920x1080'
12+
]});
13+
const page = await browser.newPage();
14+
await page.setViewport({ width: 1920, height: 1080 });
15+
await page.goto('https://ui.perfetto.dev');
16+
// Wait for the home page to be visible
17+
console.log('Waiting for page to load...');
18+
await page.waitForSelector('.pf-home-page', { visible: true, timeout: 30000 });
19+
// Locate and click the Open trace button
20+
const elements = await page.$$('li');
21+
let element = null;
22+
for (const el of elements) {
23+
const text = await el.evaluate(node => node.textContent);
24+
if (text && text.includes('Open trace file')) {
25+
element = el;
26+
break;
27+
}
28+
}
29+
if (element) {
30+
const [fileChooser] = await Promise.all([
31+
page.waitForFileChooser(),
32+
element.click()
33+
]);
34+
await fileChooser.accept(['/workspace/ck_build_trace.json']);
35+
} else {
36+
throw new Error('Element not found');
37+
}
38+
console.log('Waiting for data to load...');
39+
// Wait for the timeline element to be visible
40+
await page.waitForSelector('.pf-track', { timeout: 30000 });
41+
// Wait for the data to finish loading
42+
await page.waitForFunction(() => {
43+
return !document.body.textContent.includes('Loading...');
44+
}, { timeout: 30000 });
45+
console.log('Capturing screenshot...');
46+
await page.screenshot({path: '/workspace/perfetto_snapshot_build.png'});
47+
console.log('Done capturing screenshot...');
48+
await browser.close();
49+
} catch (err) {
50+
console.error(err);
51+
process.exit(1);
52+
}
53+
})();

0 commit comments

Comments
 (0)