Skip to content

Commit fa1ab09

Browse files
Merge pull request #1 from browserstack/INTG-2133_add_cad_reporting
Intg 2133 add cad reporting
2 parents 0edd139 + 77c24a9 commit fa1ab09

File tree

1 file changed

+223
-0
lines changed

1 file changed

+223
-0
lines changed

browserstack_ci_template.yml

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
.set_browserstack_config:
2+
image: alpine:latest
3+
script: |
4+
echo "Setting Browserstack Config vars..."
5+
6+
# Validate required variables
7+
if [ -z "$BROWSERSTACK_USERNAME" ]; then
8+
echo "Error: BROWSERSTACK_USERNAME is not set!"
9+
exit 1
10+
fi
11+
12+
if [ -z "$BROWSERSTACK_ACCESS_KEY" ]; then
13+
echo "Error: BROWSERSTACK_ACCESS_KEY is not set!"
14+
exit 1
15+
fi
16+
17+
# Construct dynamic build name
18+
export BROWSERSTACK_BUILD_NAME="gitlab-ci-${CI_PROJECT_NAME}-${CI_PIPELINE_IID}"
19+
20+
# Write to browserstack_vars.env
21+
echo "BROWSERSTACK_USERNAME=${BROWSERSTACK_USERNAME}" > browserstack_vars.env
22+
echo "BROWSERSTACK_ACCESS_KEY=${BROWSERSTACK_ACCESS_KEY}" >> browserstack_vars.env
23+
echo "BROWSERSTACK_BUILD_NAME=${BROWSERSTACK_BUILD_NAME}" >> browserstack_vars.env
24+
cat browserstack_vars.env
25+
artifacts:
26+
paths:
27+
- browserstack_vars.env
28+
29+
.set_browserstack_test_report:
30+
image: alpine:latest
31+
script: |
32+
echo "Installing bash, curl & jq"
33+
apk add --no-cache bash curl jq
34+
bash <<'EOF'
35+
#!/bin/bash
36+
source browserstack_vars.env
37+
API_PATH="https://api-observability.browserstack.com/ext/v1/builds/buildReport"
38+
REPORT_STATUS_COMPLETED="COMPLETED"
39+
REPORT_STATUS_NOT_AVAILABLE="NOT_AVAILABLE"
40+
REPORT_STATUS_TEST_AVAILABLE="TEST_AVAILABLE"
41+
REPORT_STATUS_IN_PROGRESS="IN_PROGRESS"
42+
REQUESTING_CI="gitlab-ci"
43+
REPORT_FORMAT='["plainText", "richHtml"]'
44+
45+
# Error scenario mappings
46+
declare -A ERROR_SCENARIOS=(
47+
["BUILD_NOT_FOUND"]="Build not found in BrowserStack"
48+
["MULTIPLE_BUILD_FOUND"]="Multiple builds found with the same name"
49+
["DATA_NOT_AVAILABLE"]="Report data not available from BrowserStack"
50+
)
51+
52+
# Check if BROWSERSTACK_BUILD_NAME is set
53+
if [[ -z "$BROWSERSTACK_BUILD_NAME" ]]; then
54+
echo "Error: BROWSERSTACK_BUILD_NAME is not set."
55+
exit 0
56+
fi
57+
58+
if [[ -z "$REPORT_TIMEOUT" ]]; then
59+
REPORT_TIMEOUT=130
60+
fi
61+
62+
if [[ "$REPORT_TIMEOUT" -lt 20 || "$REPORT_TIMEOUT" -gt 600 ]]; then
63+
echo "Error: REPORT_TIMEOUT must be between 20 and 600 seconds."
64+
exit 1
65+
fi
66+
67+
# Function to make API requests
68+
make_api_request() {
69+
local request_type=$1
70+
local auth_header
71+
local header_file
72+
local response
73+
74+
# Encode username:accesskey to base64
75+
auth_header=$(echo -n "${BROWSERSTACK_USERNAME}:${BROWSERSTACK_ACCESS_KEY}" | base64)
76+
# Create a temporary file for headers
77+
header_file=$(mktemp)
78+
response=$(curl -s -w "%{http_code}" -X POST "$API_PATH" \
79+
-H "Content-Type: application/json" \
80+
-H "Authorization: Basic $auth_header" \
81+
-D "$header_file" \
82+
-d "{
83+
\"originalBuildName\": \"${BROWSERSTACK_BUILD_NAME}\",
84+
\"buildStartedAt\": \"$(date +%s)\",
85+
\"requestingCi\": \"$REQUESTING_CI\",
86+
\"reportFormat\": $REPORT_FORMAT,
87+
\"requestType\": \"$request_type\",
88+
\"userTimeout\": \"${REPORT_TIMEOUT}\"
89+
}")
90+
91+
# Extract the HTTP status code from the response
92+
local http_status=${response: -3}
93+
# Extract the response body (everything except the last 3 characters)
94+
local body=${response:0:${#response}-3}
95+
96+
# Clean up the temporary file
97+
rm -f "$header_file"
98+
99+
if [[ -z "$body" ]]; then
100+
body='""'
101+
fi
102+
103+
# Return both status code and body as a JSON object
104+
echo "{\"status_code\": $http_status, \"body\": $body}"
105+
}
106+
107+
# Function to extract report data
108+
extract_report_data() {
109+
local response=$1
110+
rich_html_response=$(echo "$response" | jq -r '.report.richHtml // empty')
111+
rich_css_response=$(echo "$response" | jq -r '.report.richCss // empty')
112+
plain_text_response=$(echo "$response" | jq -r '.report.plainText // empty')
113+
}
114+
115+
# Function to check report status
116+
check_report_status() {
117+
local response=$1
118+
local status_code
119+
local body
120+
local error_message
121+
122+
status_code=$(echo "$response" | jq -r '.status_code')
123+
body=$(echo "$response" | jq -r '.body')
124+
125+
if [[ $status_code -ne 200 ]]; then
126+
echo "Error: API returned status code $status_code"
127+
error_message=$(echo "$body" | jq -r '.message // "Unknown error"')
128+
echo "Error message: $error_message"
129+
return 1
130+
fi
131+
132+
REPORT_STATUS=$(echo "$body" | jq -r '.reportStatus // empty')
133+
if [[ "$REPORT_STATUS" == "$REPORT_STATUS_COMPLETED" ||
134+
"$REPORT_STATUS" == "$REPORT_STATUS_TEST_AVAILABLE" ||
135+
"$REPORT_STATUS" == "$REPORT_STATUS_NOT_AVAILABLE" ]]; then
136+
extract_report_data "$body"
137+
return 0
138+
fi
139+
return 2
140+
}
141+
echo "Build Name: $BROWSERSTACK_BUILD_NAME will be used to fetch the report."
142+
echo "Making initial API request to Browserstack Test Report API..."
143+
144+
# Initial API Request
145+
RESPONSE=$(make_api_request "FIRST")
146+
check_report_status "$RESPONSE" || true
147+
RETRY_COUNT=$(echo "$RESPONSE" | jq -r '.body.retryCount // 3')
148+
POLLING_DURATION=$(echo "$RESPONSE" | jq -r '.body.pollingInterval // 3')
149+
150+
# Polling Mechanism
151+
[ "$REPORT_STATUS" == "$REPORT_STATUS_IN_PROGRESS" ] && {
152+
echo "Starting polling mechanism to fetch Test report..."
153+
}
154+
local_retry=0
155+
ELAPSED_TIME=0
156+
157+
while [[ $local_retry -lt $RETRY_COUNT && $REPORT_STATUS == "$REPORT_STATUS_IN_PROGRESS" ]]; do
158+
if [[ -n "$REPORT_TIMEOUT" && "$ELAPSED_TIME" -ge "$REPORT_TIMEOUT" ]]; then
159+
echo "User report timeout reached. Making final API request..."
160+
RESPONSE=$(make_api_request "LAST")
161+
check_report_status "$RESPONSE" && break
162+
break
163+
fi
164+
165+
ELAPSED_TIME=$((ELAPSED_TIME + POLLING_DURATION))
166+
local_retry=$((local_retry + 1))
167+
168+
RESPONSE=$(make_api_request "POLL")
169+
echo "Polling attempt $local_retry/$RETRY_COUNT"
170+
# Stop polling if API response is non-200
171+
status_code=$(echo "$RESPONSE" | jq -r '.status_code')
172+
if [[ $status_code -ne 200 ]]; then
173+
echo "Polling stopped due to non-200 response from API. Status code: $status_code"
174+
break
175+
fi
176+
177+
check_report_status "$RESPONSE" && {
178+
echo "Valid report status received. Exiting polling loop...."
179+
break
180+
}
181+
182+
sleep "$POLLING_DURATION"
183+
done
184+
# Handle Report
185+
if [[ -n "$rich_html_response" ]]; then
186+
# Embed CSS into the rich HTML report
187+
mkdir -p browserstack
188+
echo "<!DOCTYPE html>
189+
<html>
190+
<head>
191+
<style>
192+
$rich_css_response
193+
</style>
194+
</head>
195+
$rich_html_response
196+
</html>" > browserstack/testreport.html
197+
echo "Rich html report saved as browserstack/testreport.html. To download the report, open artifacts tab & go to a job which extends set_browserstack_test_report job."
198+
199+
# Generate plain text report
200+
if [[ -n "$plain_text_response" ]]; then
201+
echo ""
202+
echo "Browserstack textual report"
203+
echo "$plain_text_response"
204+
else
205+
echo "Plain text response is empty."
206+
fi
207+
elif [[ "$REPORT_STATUS" == "$REPORT_STATUS_NOT_AVAILABLE" ]]; then
208+
error_reason=$(echo "$RESPONSE" | jq -r '.body.errorReason // empty')
209+
default_error_message="Failed to retrieve report. Reason:"
210+
if [[ -n "$error_reason" ]]; then
211+
echo "$default_error_message ${ERROR_SCENARIOS[$error_reason]:-$error_reason}"
212+
else
213+
echo "$default_error_message Unexpected error"
214+
fi
215+
else
216+
echo "Failed to retrieve report."
217+
fi
218+
# Ensure pipeline doesn't exit with non-zero status
219+
exit 0
220+
EOF
221+
artifacts:
222+
paths:
223+
- browserstack/testreport.html

0 commit comments

Comments
 (0)