From 44c8094513389c909fced2900bcd3a2b97a9f5aa Mon Sep 17 00:00:00 2001 From: Deborah Jacob Date: Fri, 6 Feb 2026 11:52:55 -0500 Subject: [PATCH 1/2] Fix release workflow, remove duplicate publish.yml - Fix logic bug in testpypi condition (missing parentheses) - Remove redundant publish.yml (release.yml handles both targets) Signed-off-by: Claude Opus 4.5 Signed-off-by: Deborah Jacob --- .github/workflows/publish.yml | 81 ----------------------------------- .github/workflows/release.yml | 4 +- 2 files changed, 2 insertions(+), 83 deletions(-) delete mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index afbd8fc..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,81 +0,0 @@ -# SPDX-FileCopyrightText: 2026 The Botanu Authors -# SPDX-License-Identifier: Apache-2.0 - -name: Publish to PyPI - -on: - release: - types: [published] - workflow_dispatch: - inputs: - target: - description: 'Target repository' - required: true - default: 'testpypi' - type: choice - options: - - testpypi - - pypi - -permissions: - contents: read - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: actions/setup-python@v5 - with: - python-version: "3.12" - - - name: Install build dependencies - run: pip install build - - - name: Build package - run: python -m build - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: dist - path: dist/ - - publish-testpypi: - needs: build - runs-on: ubuntu-latest - if: github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'testpypi' - environment: testpypi - permissions: - id-token: write - steps: - - name: Download artifacts - uses: actions/download-artifact@v4 - with: - name: dist - path: dist/ - - - name: Publish to TestPyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - repository-url: https://test.pypi.org/legacy/ - - publish-pypi: - needs: build - runs-on: ubuntu-latest - if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.event.inputs.target == 'pypi') - environment: pypi - permissions: - id-token: write - steps: - - name: Download artifacts - uses: actions/download-artifact@v4 - with: - name: dist - path: dist/ - - - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a2ed4b5..266eda0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -61,9 +61,9 @@ jobs: # ------------------------------------------------------------------- publish-testpypi: needs: build - if: | + if: >- github.event_name == 'workflow_dispatch' && github.event.inputs.publish_target == 'testpypi' - || (github.event_name == 'push' && contains(github.ref, '-alpha') || contains(github.ref, '-beta') || contains(github.ref, '-rc')) + || (github.event_name == 'push' && (contains(github.ref, '-alpha') || contains(github.ref, '-beta') || contains(github.ref, '-rc'))) runs-on: ubuntu-latest environment: name: testpypi From 63c860e6c6fa3b33a5ebda8e20dee06e3f77524b Mon Sep 17 00:00:00 2001 From: Deborah Jacob Date: Fri, 6 Feb 2026 12:05:03 -0500 Subject: [PATCH 2/2] Remove orphaned metrics.py module This module was no longer used after the decision to derive all cost metrics from 100% trace capture. The backend aggregates costs from traces by run_id, making standalone metrics redundant. Signed-off-by: Claude Opus 4.5 Signed-off-by: Deborah Jacob --- src/botanu/tracking/metrics.py | 90 ---------------------------------- 1 file changed, 90 deletions(-) delete mode 100644 src/botanu/tracking/metrics.py diff --git a/src/botanu/tracking/metrics.py b/src/botanu/tracking/metrics.py deleted file mode 100644 index 89e34b8..0000000 --- a/src/botanu/tracking/metrics.py +++ /dev/null @@ -1,90 +0,0 @@ -# SPDX-FileCopyrightText: 2026 The Botanu Authors -# SPDX-License-Identifier: Apache-2.0 - -"""Run metrics — reliable aggregates for dashboards and alerts. - -Metrics are the "always-on truth" — they're not sampled like spans. - -- ``botanu.run.completed`` (counter): Total runs by use_case, status, environment -- ``botanu.run.duration_ms`` (histogram): Run duration distribution -""" - -from __future__ import annotations - -import logging -from typing import Optional - -from opentelemetry import metrics -from opentelemetry.metrics import Counter, Histogram - -logger = logging.getLogger(__name__) - -_SDK_METER_NAME = "botanu_sdk" - -meter = metrics.get_meter(_SDK_METER_NAME) - -_run_completed_counter: Optional[Counter] = None -_run_duration_histogram: Optional[Histogram] = None - - -def _get_run_completed_counter() -> Counter: - global _run_completed_counter - if _run_completed_counter is None: - _run_completed_counter = meter.create_counter( - name="botanu.run.completed", - description="Total number of completed runs", - unit="1", - ) - return _run_completed_counter - - -def _get_run_duration_histogram() -> Histogram: - global _run_duration_histogram - if _run_duration_histogram is None: - _run_duration_histogram = meter.create_histogram( - name="botanu.run.duration_ms", - description="Run duration in milliseconds", - unit="ms", - ) - return _run_duration_histogram - - -def record_run_completed( - use_case: str, - status: str, - environment: str, - duration_ms: float, - service_name: Optional[str] = None, - workflow: Optional[str] = None, -) -> None: - """Record a completed run in metrics. - - Called at the end of every run, regardless of whether the span is sampled. - - Args: - use_case: Use case name (low cardinality). - status: Outcome status (success/failure/partial/timeout/canceled). - environment: Deployment environment. - duration_ms: Run duration in milliseconds. - service_name: Service name (optional). - workflow: Workflow name (optional). - """ - attrs = { - "use_case": use_case, - "status": status, - "environment": environment, - } - if service_name: - attrs["service.name"] = service_name - if workflow: - attrs["workflow"] = workflow - - try: - _get_run_completed_counter().add(1, attrs) - except Exception as exc: - logger.debug("Failed to record run.completed metric: %s", exc) - - try: - _get_run_duration_histogram().record(duration_ms, attrs) - except Exception as exc: - logger.debug("Failed to record run.duration_ms metric: %s", exc)