Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
571 changes: 10 additions & 561 deletions .github/MONITORING_GUIDE.md

Large diffs are not rendered by default.

57 changes: 30 additions & 27 deletions .github/workflows/weekly-health.yml
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
name: Weekly SDK Health Check
name: Scheduled SDK Synthetic

on:
schedule:
- cron: "0 14 * * 1" # Monday 6am PST / 2pm UTC
- cron: "17 * * * *"
workflow_dispatch: {}

permissions:
contents: read

concurrency:
group: python-sdk-synthetic
cancel-in-progress: false

jobs:
health-check:
name: Integration Health Check
name: Latest + bounded history
runs-on: ubuntu-latest
timeout-minutes: 10
env:
# Both names on purpose: the integration/contract conftests read
# OILPRICEAPI_KEY, while the live futures/subscriptions/well-production
# tests read OILPRICEAPI_TEST_KEY. Exporting only one silently skipped
# the other half of the suite (#48).
OILPRICEAPI_KEY: ${{ secrets.OILPRICEAPI_TEST_KEY }}
OILPRICEAPI_TEST_KEY: ${{ secrets.OILPRICEAPI_TEST_KEY }}

steps:
- uses: actions/checkout@v7
Expand All @@ -30,31 +33,31 @@ jobs:
python -m pip install --upgrade pip
pip install -e '.[dev]'

# Tier 1 (#48): keyless demo smoke — no secret, no gate, cannot
# silently skip even if the repo secret disappears.
- name: Keyless demo smoke (always runs)
run: pytest tests/integration/test_demo_contract.py -m live --no-cov -v --timeout=60

# Guard at the shell level so the job passes loudly (::warning::) instead
# of failing or silently skipping every test if the secret is empty.
- name: Run integration tests
- name: Require monitor credential
run: |
if [ -z "$OILPRICEAPI_KEY" ]; then
echo "::warning::OILPRICEAPI_TEST_KEY secret is empty/unset - live integration tests skipped"
exit 0
echo "::error::OILPRICEAPI_TEST_KEY is empty or unset"
exit 1
fi
pytest tests/ -m 'integration' -v --no-cov --timeout=60

- name: Run contract tests
run: |
if [ -z "$OILPRICEAPI_KEY" ]; then
echo "::warning::OILPRICEAPI_TEST_KEY secret is empty/unset - contract tests skipped"
exit 0
fi
pytest tests/ -m 'contract' -v --no-cov --timeout=60
- name: Run bounded customer-path synthetic
run: python scripts/synthetic_monitor.py --output artifacts/sdk-health.json

- name: Check for dependency vulnerabilities
- name: Publish receipt in job summary
if: always() && hashFiles('artifacts/sdk-health.json') != ''
run: |
pip install pip-audit
pip-audit --strict
continue-on-error: true
echo '### Python SDK synthetic receipt' >> "$GITHUB_STEP_SUMMARY"
echo '```json' >> "$GITHUB_STEP_SUMMARY"
cat artifacts/sdk-health.json >> "$GITHUB_STEP_SUMMARY"
echo '```' >> "$GITHUB_STEP_SUMMARY"

- name: Upload 30-day receipt
if: always() && hashFiles('artifacts/sdk-health.json') != ''
uses: actions/upload-artifact@v7
with:
name: sdk-health-${{ github.run_id }}
path: artifacts/sdk-health.json
retention-days: 30
22 changes: 0 additions & 22 deletions Dockerfile.monitor

This file was deleted.

103 changes: 0 additions & 103 deletions docker-compose.monitoring.yml

This file was deleted.

41 changes: 41 additions & 0 deletions docs/SYNTHETIC_MONITORING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Python SDK synthetic monitoring

The `Scheduled SDK Synthetic` workflow runs once an hour and validates the two
smallest customer-critical paths through the installed SDK:

1. a latest `BRENT_CRUDE_USD` price with a finite numeric value, currency, unit,
and source timestamp;
2. a five-record daily history request with finite numeric values.

A keyless demo-contract test runs first. The keyed check fails loudly when the
monitor credential is missing; it never silently skips.

## Receipts and alerts

Each run writes `sdk-health.json`, publishes it in the GitHub Actions job
summary, and retains it as an artifact for 30 days. Receipts contain the SDK
version, check names, durations, and structural assertions. They exclude the API
key, response values, request URLs, response bodies, and exception messages.

GitHub Actions run history is the dashboard. A failed scheduled workflow is the
alert and uses the repository notification settings; the workflow deliberately
does not create recurring GitHub issues.

## Response runbook

1. Open the failed `Scheduled SDK Synthetic` run and read its JSON receipt.
2. If `configuration` failed, restore or rotate `OILPRICEAPI_TEST_KEY`, then run
the workflow manually.
3. If only the keyless demo check failed, verify public API availability and
response-envelope drift.
4. If `latest_price` or `bounded_history` failed, compare with the latest
`Live API Tests` run and reproduce with a non-customer test credential.
5. Treat repeated time-budget failures as a latency regression. Treat missing
fields, empty history, or non-finite values as a contract regression.
6. Record any product incident in the owning API repository. Keep SDK parsing,
retry, or compatibility fixes in this repository.

The schedule is hourly rather than every five minutes. Two authenticated checks
per hour provide continuous SDK-contract coverage without spending thousands of
CI minutes or consuming unnecessary API quota. Push and pull-request live tests
provide additional coverage between scheduled runs.
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ theme:
nav:
- Home: index.md
- DataFrames and Pagination: DATAFRAMES.md
- Synthetic Monitoring: SYNTHETIC_MONITORING.md
- Performance Guide: PERFORMANCE_GUIDE.md
- API Reference:
- Client: reference/client.md
Expand Down
Loading