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
85 changes: 69 additions & 16 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -1,43 +1,96 @@
name: Linting

# Disabled: requires checkout of private makegov/tango repo for filter_shape conformance check.
# Re-enable when that repo is accessible (e.g. add TANGO_API_REPO_ACCESS_TOKEN secret).
# Lint gate runs on every PR and push to main.
#
# - ruff format + ruff check are HARD gates (block the PR).
# - mypy is ADVISORY for now (continue-on-error): the package carries ~28
# pre-existing type errors that predate CI enforcement. Tracked for burn-down
# in makegov/tango-python; flip `continue-on-error` off once that's clear.
# - The SDK filter/shape conformance check needs the canonical manifest from the
# private makegov/tango repo, which requires a TANGO_API_REPO_ACCESS_TOKEN
# secret the public CI does not have. The conformance job SKIPS cleanly when
# the token is absent (rather than failing red) and becomes a real gate the
# moment the secret is configured.
on:
workflow_dispatch:
# push:
# branches: [ main, develop ]
# pull_request:
# branches: [ main, develop ]
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
lint:
runs-on: ubuntu-latest


steps:
- uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v4
with:
version: "latest"

- name: Set up Python
run: uv python install 3.12

- name: Install dependencies
run: uv sync --all-extras

- name: Check formatting with ruff
run: uv run ruff format --check tango/

- name: Lint with ruff
run: uv run ruff check tango/

- name: Type check with mypy (advisory)
continue-on-error: true
run: uv run mypy tango/

conformance:
# Requires the canonical filter_shape manifest from the private makegov/tango
# repo. When TANGO_API_REPO_ACCESS_TOKEN is not configured, every real step
# is skipped and the job passes (rather than failing on an empty token).
# Configure the secret to turn this into a hard gate automatically.
runs-on: ubuntu-latest

steps:
- name: Determine token availability
id: gate
env:
TANGO_API_REPO_ACCESS_TOKEN: ${{ secrets.TANGO_API_REPO_ACCESS_TOKEN }}
run: |
if [ -n "$TANGO_API_REPO_ACCESS_TOKEN" ]; then
echo "ready=true" >> "$GITHUB_OUTPUT"
else
echo "ready=false" >> "$GITHUB_OUTPUT"
echo "::notice::Skipping SDK conformance check — TANGO_API_REPO_ACCESS_TOKEN not configured."
fi

- uses: actions/checkout@v4
if: steps.gate.outputs.ready == 'true'

- name: Checkout tango API repo (manifest source)
if: steps.gate.outputs.ready == 'true'
uses: actions/checkout@v4
with:
repository: makegov/tango
path: tango-api

token: ${{ secrets.TANGO_API_REPO_ACCESS_TOKEN }}

- name: Install uv
if: steps.gate.outputs.ready == 'true'
uses: astral-sh/setup-uv@v4
with:
version: "latest"

- name: Set up Python
if: steps.gate.outputs.ready == 'true'
run: uv python install 3.12

- name: Install dependencies
if: steps.gate.outputs.ready == 'true'
run: uv sync --all-extras

- name: Check formatting with ruff
run: uv run ruff format --check tango/

- name: Lint with ruff
run: uv run ruff check tango/

- name: Check SDK filter/shape conformance
if: steps.gate.outputs.ready == 'true'
run: uv run python scripts/check_filter_shape_conformance.py --manifest tango-api/contracts/filter_shape_contract.json

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
45 changes: 45 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,51 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed
- `_parse_webhook_alert`: aligned the parser output with the `WebhookAlert`
type contract. Sparse server payloads now hydrate `query_type` and `filters`
as `""` / `{}` (matching their declared non-null types) rather than `None`,
and `status` is typed as the `Literal["active", "paused"]` the model promises.
Clears the four type-check errors this introduced; no change for full payloads.
- `Contract` model: removed dead fields (`id`, `award_id`, `recipient_name`,
`award_amount`, `awarding_agency`, `funding_agency`) and added the real API
fields (`key`, `piid`, `obligated`, `total_contract_value`,
`base_and_exercised_options_value`, `awarding_office`, `funding_office`,
`naics_code`, `psc_code`, `set_aside`, `solicitation_identifier`,
`parent_award`, `legislative_mandates`, `subawards_summary`,
`place_of_performance`). The deprecated fields remain declared with `None`
defaults for one minor cycle (they never returned data) and will be removed
in `2.0.0`. New `OrganizationOfficePayload` dataclass for the office fields.
(`Contract` is a documentation-only dataclass — not instantiated or exported
— so there is no runtime impact.)
- `list_contracts`: no longer sends `page=1` to the cursor-only `/api/contracts/`
endpoint. When no cursor is supplied, neither `page` nor `cursor` is sent and
the API returns the first page by default.
- Shape validation: registered the `ContractOrIDVCompetition` nested schema
(alias of `Competition`) so nested selections like
`competition(extent_competed,number_of_offers_received)` on contract / IDV
shapes validate instead of raising `ShapeValidationError`.

### Added
- Budget accounts surface (tango v4.6.8): `list_budget_accounts`,
`get_budget_account`, `get_budget_account_quarters`,
`get_budget_account_recipients`. New `BudgetAccount` dataclass exported from
the top-level package, plus `ShapeConfig.BUDGET_ACCOUNTS_MINIMAL`.
- Singleton detail GETs: `get_contract`, `get_contract_subawards`,
`get_contract_transactions`, `get_forecast`, `get_grant`, `get_notice`,
`get_opportunity`, `get_subaward`.
- `get_entity_budget_flows(uei)` for `/api/entities/{uei}/budget-flows/`.
- `list_otidv_awards(key)` for `/api/otidvs/{key}/awards/` (parity with Node).
- `grant_id` filter kwarg on `list_grants` (supports multi-value OR via `|`).

### CI
- Re-enabled `lint.yml` as a PR + push-to-main gate. `ruff format` and
`ruff check` are hard gates; `mypy` runs advisory (continue-on-error) pending
burn-down of ~28 pre-existing type errors. The filter/shape conformance check
is a separate job that skips cleanly until a `TANGO_API_REPO_ACCESS_TOKEN`
secret for the private manifest repo is configured, at which point it becomes
a hard gate.

## [1.0.0] - 2026-05-13

> First stable release. `tango-python` is now at full API parity with the
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "tango-python"
version = "1.0.0"
version = "1.1.0"
description = "Python SDK for the Tango API"
readme = "README.md"
requires-python = ">=3.12"
Expand Down
3 changes: 3 additions & 0 deletions scripts/check_filter_shape_conformance.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"naics": "list_naics",
"gsa_elibrary_contracts": "list_gsa_elibrary_contracts",
"itdashboard": "list_itdashboard_investments",
"budget_accounts": "list_budget_accounts",
# Resources not yet implemented in SDK
"offices": None,
}
Expand All @@ -62,6 +63,7 @@ def get_shape_config_entries() -> list[tuple[str, str, type[Any]]]:
IDV,
OTA,
OTIDV,
BudgetAccount,
Contract,
Entity,
Forecast,
Expand Down Expand Up @@ -110,6 +112,7 @@ def get_shape_config_entries() -> list[tuple[str, str, type[Any]]]:
ShapeConfig.ITDASHBOARD_INVESTMENTS_COMPREHENSIVE,
ITDashboardInvestment,
),
("BUDGET_ACCOUNTS_MINIMAL", ShapeConfig.BUDGET_ACCOUNTS_MINIMAL, BudgetAccount),
]
for name, shape_str, model_cls in configs:
entries.append((name, shape_str, model_cls))
Expand Down
4 changes: 3 additions & 1 deletion tango/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
TangoValidationError,
)
from .models import (
BudgetAccount,
GsaElibraryContract,
ITDashboardInvestment,
PaginatedResponse,
Expand Down Expand Up @@ -43,7 +44,7 @@
)
from .webhooks.receiver import Delivery, WebhookReceiver

__version__ = "1.0.0"
__version__ = "1.1.0"
__all__ = [
"TangoClient",
"TangoAPIError",
Expand All @@ -54,6 +55,7 @@
"RateLimitInfo",
"ResolveCandidate",
"ResolveResult",
"BudgetAccount",
"GsaElibraryContract",
"ITDashboardInvestment",
"PaginatedResponse",
Expand Down
Loading
Loading