2525 BETTERSTACK_HEARTBEAT_URL_HE_TME_STAGING :
2626 # Optional until the HE-TME Better Stack monitor is created; the heartbeat step skips gracefully when absent.
2727 required : false
28+ BETTERSTACK_HEARTBEAT_URL_PLATFORM_API_STAGING :
29+ # Optional until the Platform API Better Stack monitor is created; the heartbeat step skips gracefully when absent.
30+ required : false
2831 AIGNOSTICS_CLIENT_ID_DEVICE_PRODUCTION :
2932 required : true
3033 AIGNOSTICS_REFRESH_TOKEN_PRODUCTION :
3639 BETTERSTACK_HEARTBEAT_URL_HE_TME_PRODUCTION :
3740 # Optional until the HE-TME Better Stack monitor is created; the heartbeat step skips gracefully when absent.
3841 required : false
42+ BETTERSTACK_HEARTBEAT_URL_PLATFORM_API_PRODUCTION :
43+ # Optional until the Platform API Better Stack monitor is created; the heartbeat step skips gracefully when absent.
44+ required : false
3945 SENTRY_DSN :
4046 required : true
4147
@@ -96,10 +102,12 @@ jobs:
96102 echo "$GCP_CREDENTIALS" | base64 -d > credentials.json
97103 echo "GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/credentials.json" >> $GITHUB_ENV
98104
99- # Tests are split into two runs so failures can be routed to the correct Better Stack monitor:
100- # SDK-layer failures (auth, listing, connectivity) go to the SDK monitor; HE-TME application
101- # failures go to the HE-TME monitor. Tests declare which system they monitor via
102- # @pytest.mark.monitors("he-tme"); tests without that marker are SDK health checks.
105+ # Tests are split into three runs so failures can be routed to the correct Better Stack monitor:
106+ # - SDK-layer failures (token management, service wiring): no monitors marker → SDK monitor
107+ # - Platform API failures (auth, listing, connectivity): monitors_platform_api → Platform API monitor
108+ # - Application failures (HE-TME, test-app processing): monitors("he-tme"/"test-app") → HE-TME monitor
109+ # Tests declare which system they monitor via @pytest.mark.monitors("he-tme") /
110+ # @pytest.mark.monitors_platform_api; tests with neither marker are SDK health checks.
103111 - name : Test / scheduled / sdk
104112 id : test_sdk
105113 env :
@@ -110,10 +118,24 @@ jobs:
110118 # manually and send it to Better Stack regardless of outcome.
111119 set +e
112120 XDIST_WORKER_FACTOR=1 uv run --all-extras nox -s test -- \
113- -m "(scheduled or scheduled_only) and not monitors and not stress_only" \
121+ -m "(scheduled or scheduled_only) and not monitors and not monitors_platform_api and not stress_only" \
114122 --junit-xml=reports/junit_sdk.xml
115123 echo "exit_code=$?" >> $GITHUB_OUTPUT
116124
125+ - name : Test / scheduled / platform-api
126+ id : test_platform_api
127+ env :
128+ SENTRY_DSN : ${{ secrets.SENTRY_DSN }}
129+ shell : bash
130+ run : |
131+ # set +e so a test failure does not abort the step — we capture the exit code
132+ # manually and send it to Better Stack regardless of outcome.
133+ set +e
134+ XDIST_WORKER_FACTOR=1 uv run --all-extras nox -s test -- \
135+ -m "(scheduled or scheduled_only) and monitors_platform_api and not stress_only" \
136+ --junit-xml=reports/junit_platform_api.xml
137+ echo "exit_code=$?" >> $GITHUB_OUTPUT
138+
117139 - name : Test / scheduled / he-tme
118140 id : test_he_tme
119141 env :
@@ -124,7 +146,7 @@ jobs:
124146 # manually and send it to Better Stack regardless of outcome.
125147 set +e
126148 XDIST_WORKER_FACTOR=1 uv run --all-extras nox -s test -- \
127- -m "(scheduled or scheduled_only) and monitors and not stress_only" \
149+ -m "(scheduled or scheduled_only) and monitors and not monitors_platform_api and not stress_only" \
128150 --junit-xml=reports/junit_he_tme.xml
129151 echo "exit_code=$?" >> $GITHUB_OUTPUT
130152
@@ -135,14 +157,16 @@ jobs:
135157 # Default to 1 (failure) if a step never wrote its output — e.g. a setup step
136158 # errored before tests ran. Prevents sending a false "healthy" heartbeat.
137159 SDK_EXIT=${{ steps.test_sdk.outputs.exit_code || '1' }}
160+ PLATFORM_API_EXIT=${{ steps.test_platform_api.outputs.exit_code || '1' }}
138161 HE_TME_EXIT=${{ steps.test_he_tme.outputs.exit_code || '1' }}
139- # Combined exit code: non-zero if either run failed
140- if [ "$SDK_EXIT" != "0" ] || [ "$HE_TME_EXIT" != "0" ]; then
162+ # Combined exit code: non-zero if any run failed
163+ if [ "$SDK_EXIT" != "0" ] || [ "$PLATFORM_API_EXIT" != "0" ] || [ "$ HE_TME_EXIT" != "0" ]; then
141164 COMBINED_EXIT=1
142165 else
143166 COMBINED_EXIT=0
144167 fi
145168 echo "sdk_exit=${SDK_EXIT}" >> $GITHUB_OUTPUT
169+ echo "platform_api_exit=${PLATFORM_API_EXIT}" >> $GITHUB_OUTPUT
146170 echo "he_tme_exit=${HE_TME_EXIT}" >> $GITHUB_OUTPUT
147171 echo "combined_exit=${COMBINED_EXIT}" >> $GITHUB_OUTPUT
148172
@@ -229,6 +253,56 @@ jobs:
229253 "${BETTERSTACK_HEARTBEAT_URL}/${SDK_EXIT}"
230254 echo "INFO: Sent SDK heartbeat to BetterStack with exit code '${SDK_EXIT}'"
231255
256+ - name : Heartbeat / BetterStack / Platform API
257+ if : always()
258+ shell : bash
259+ env :
260+ BETTERSTACK_HEARTBEAT_URL_PLATFORM_API : " ${{ inputs.platform_environment == 'staging' && secrets.BETTERSTACK_HEARTBEAT_URL_PLATFORM_API_STAGING || secrets.BETTERSTACK_HEARTBEAT_URL_PLATFORM_API_PRODUCTION }}"
261+ PLATFORM_API_EXIT : ${{ steps.collect.outputs.platform_api_exit }}
262+ run : |
263+ if [ -z "$BETTERSTACK_HEARTBEAT_URL_PLATFORM_API" ]; then
264+ echo "INFO: No BetterStack Platform API heartbeat URL configured, skipped."
265+ exit 0
266+ fi
267+ BETTERSTACK_METADATA_PAYLOAD=$(jq -n \
268+ --arg github_workflow "${{ github.workflow }}" \
269+ --arg github_run_url "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" \
270+ --arg github_run_id "${{ github.run_id }}" \
271+ --arg github_job "${{ github.job }}" \
272+ --arg github_sha "${{ github.sha }}" \
273+ --arg github_actor "${{ github.actor }}" \
274+ --arg github_repository "${{ github.repository }}" \
275+ --arg github_ref "${{ github.ref }}" \
276+ --arg job_status "${{ job.status }}" \
277+ --arg github_event_name "${{ github.event_name }}" \
278+ --arg timestamp "$(date -u +"%Y-%m-%dT%H:%M:%SZ")" \
279+ '{
280+ github: {
281+ workflow: $github_workflow,
282+ run_url: $github_run_url,
283+ run_id: $github_run_id,
284+ job: $github_job,
285+ sha: $github_sha,
286+ actor: $github_actor,
287+ repository: $github_repository,
288+ ref: $github_ref,
289+ event_name: $github_event_name
290+ },
291+ job: {
292+ status: $job_status,
293+ },
294+ timestamp: $timestamp,
295+ }'
296+ )
297+ curl \
298+ --fail-with-body \
299+ --silent \
300+ --request POST \
301+ --header "Content-Type: application/json" \
302+ --data-binary "${BETTERSTACK_METADATA_PAYLOAD}" \
303+ "${BETTERSTACK_HEARTBEAT_URL_PLATFORM_API}/${PLATFORM_API_EXIT}"
304+ echo "INFO: Sent Platform API heartbeat to BetterStack with exit code '${PLATFORM_API_EXIT}'"
305+
232306 - name : Heartbeat / BetterStack / HE-TME
233307 if : always()
234308 shell : bash
@@ -286,6 +360,7 @@ jobs:
286360 name : test-results-scheduled
287361 path : |
288362 reports/junit_sdk.xml
363+ reports/junit_platform_api.xml
289364 reports/junit_he_tme.xml
290365 reports/coverage.xml
291366 reports/coverage.md
0 commit comments