diff --git a/.github/workflows/execution-report-heartbeat.yml b/.github/workflows/execution-report-heartbeat.yml index e2e759e..42d7451 100644 --- a/.github/workflows/execution-report-heartbeat.yml +++ b/.github/workflows/execution-report-heartbeat.yml @@ -65,6 +65,14 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 + - name: Setup uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 + + - name: Install dependencies + run: | + set -euo pipefail + uv sync --frozen --no-dev + - name: Authenticate to Google Cloud id: gcp_auth_primary continue-on-error: true @@ -87,14 +95,8 @@ jobs: - name: Set up gcloud uses: google-github-actions/setup-gcloud@v3 - - name: Install market calendar - continue-on-error: true - run: >- - python -m pip install --disable-pip-version-check - --retries 3 --timeout 30 "pandas-market-calendars==5.4.0" - - name: Check recent execution report - run: python scripts/execution_report_heartbeat.py + run: uv run --no-sync python scripts/execution_report_heartbeat.py - name: Publish read-only runtime execution evidence uses: QuantStrategyLab/QuantRuntimeSettings/actions/publish-runtime-execution-evidence@769616e9b0a21b9c9f40173b87d5f3e09bbdca9e diff --git a/.github/workflows/runtime-guard.yml b/.github/workflows/runtime-guard.yml index d9744d8..3281676 100644 --- a/.github/workflows/runtime-guard.yml +++ b/.github/workflows/runtime-guard.yml @@ -63,6 +63,14 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 + - name: Setup uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 + + - name: Install dependencies + run: | + set -euo pipefail + uv sync --frozen --no-dev + - name: Authenticate to Google Cloud id: gcp_auth_primary continue-on-error: true @@ -86,4 +94,4 @@ jobs: uses: google-github-actions/setup-gcloud@v3 - name: Check Cloud Scheduler and Cloud Run logs - run: python scripts/cloud_run_runtime_guard.py + run: uv run --no-sync python scripts/cloud_run_runtime_guard.py diff --git a/.github/workflows/runtime-target-lifecycle.yml b/.github/workflows/runtime-target-lifecycle.yml index 977874c..df6e2b1 100644 --- a/.github/workflows/runtime-target-lifecycle.yml +++ b/.github/workflows/runtime-target-lifecycle.yml @@ -60,6 +60,14 @@ jobs: - name: Checkout repository uses: actions/checkout@v6 + - name: Setup uv + uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78 + + - name: Install dependencies + run: | + set -euo pipefail + uv sync --frozen --no-dev + - name: Authenticate to Google Cloud uses: google-github-actions/auth@v3 with: @@ -119,25 +127,19 @@ jobs: run: | set -uo pipefail set +e - python scripts/cloud_run_runtime_guard.py > runtime-guard.log 2>&1 + uv run --no-sync python scripts/cloud_run_runtime_guard.py > runtime-guard.log 2>&1 exit_code=$? set -e cat runtime-guard.log if [ "$exit_code" -eq 0 ]; then status=pass - elif grep -Eqi 'log query failed|internal error|network|timed out|unable to list' runtime-guard.log; then + elif grep -Eqi 'log query failed|internal error|network|timed out|unable to list|Traceback|ImportError|ModuleNotFoundError' runtime-guard.log; then status=unavailable else status=attention fi echo "status=$status" >> "$GITHUB_OUTPUT" - - name: Install market calendar for enabled heartbeat - if: ${{ steps.target_state.outputs.configured_state == 'enabled' }} - run: >- - python -m pip install --disable-pip-version-check - --retries 3 --timeout 30 "pandas-market-calendars==5.4.0" - - name: Check enabled execution heartbeat without notification side effects id: execution_heartbeat env: @@ -155,7 +157,7 @@ jobs: export RUNTIME_TARGET_JSON="$(cat runtime-target.json)" set +e - python scripts/execution_report_heartbeat.py > execution-heartbeat.log 2>&1 + uv run --no-sync python scripts/execution_report_heartbeat.py > execution-heartbeat.log 2>&1 exit_code=$? set -e cat execution-heartbeat.log @@ -163,7 +165,7 @@ jobs: status=not_due elif [ "$exit_code" -eq 0 ]; then status=pass - elif grep -Eqi 'gcloud|storage|network|timed out|internal error|unable to list' execution-heartbeat.log; then + elif grep -Eqi 'gcloud|storage|network|timed out|internal error|unable to list|Traceback|ImportError|ModuleNotFoundError' execution-heartbeat.log; then status=unavailable else status=attention diff --git a/tests/test_uv_dependency_workflow.py b/tests/test_uv_dependency_workflow.py index 007304d..3a8391c 100644 --- a/tests/test_uv_dependency_workflow.py +++ b/tests/test_uv_dependency_workflow.py @@ -12,10 +12,17 @@ def test_pyproject_declares_runtime_and_test_dependencies() -> None: assert "test = [" in pyproject -def test_ci_docker_and_env_sync_use_uv_lock() -> None: +def test_ci_docker_and_runtime_monitoring_use_uv_lock() -> None: ci = Path(".github/workflows/ci.yml").read_text(encoding="utf-8") dockerfile = Path("Dockerfile").read_text(encoding="utf-8") env_sync = Path(".github/workflows/sync-cloud-run-env.yml").read_text(encoding="utf-8") + runtime_guard = Path(".github/workflows/runtime-guard.yml").read_text(encoding="utf-8") + runtime_target_lifecycle = Path(".github/workflows/runtime-target-lifecycle.yml").read_text( + encoding="utf-8" + ) + execution_report_heartbeat = Path(".github/workflows/execution-report-heartbeat.yml").read_text( + encoding="utf-8" + ) lockfile = Path("uv.lock").read_text(encoding="utf-8") assert lockfile.startswith("version = ") @@ -24,6 +31,24 @@ def test_ci_docker_and_env_sync_use_uv_lock() -> None: assert "uv run --no-sync python external/QuantPlatformKit/scripts/check_qpk_pin_consistency.py" in ci assert "uv sync --frozen --no-dev" in env_sync assert "uv run --no-sync python scripts/build_cloud_run_env_sync_plan.py --json" in env_sync + setup_uv = "uses: astral-sh/setup-uv@37802adc94f370d6bfd71619e3f0bf239e1f3b78" + for workflow in (runtime_guard, runtime_target_lifecycle, execution_report_heartbeat): + assert setup_uv in workflow + assert workflow.count(setup_uv) == 1 + assert workflow.index(setup_uv) < workflow.index("google-github-actions/auth@v3") + assert "actions/setup-python" not in workflow + assert "python -m pip install" not in workflow + assert workflow.count("uv sync --frozen --no-dev") == 1 + assert "uv run --no-sync python scripts/cloud_run_runtime_guard.py" in runtime_guard + assert "uv run --no-sync python scripts/cloud_run_runtime_guard.py" in runtime_target_lifecycle + assert "uv run --no-sync python scripts/execution_report_heartbeat.py" in runtime_target_lifecycle + assert "uv run --no-sync python scripts/execution_report_heartbeat.py" in execution_report_heartbeat + assert "run: python scripts/cloud_run_runtime_guard.py" not in runtime_guard + assert " python scripts/cloud_run_runtime_guard.py" not in runtime_target_lifecycle + assert 'name = "pandas-market-calendars"' in lockfile + assert 'pandas-market-calendars==5.4.0' not in runtime_target_lifecycle + assert 'pandas-market-calendars==5.4.0' not in execution_report_heartbeat + assert "Traceback|ImportError|ModuleNotFoundError" in runtime_target_lifecycle assert "IBKR_RECONCILIATION_RECOVERY_STATE_LEDGER_URI" in env_sync assert "Fetch opt-in immutable recovery state ledger" in env_sync assert "gcloud storage cp --quiet" in env_sync