Skip to content
Closed
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
30 changes: 30 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: CI

on:
pull_request:
push:
branches: [main]

jobs:
test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ['3.11', '3.13']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install with the dev extra
run: python -m pip install -e ".[dev]"
- name: Tests
run: python -m pytest
# Fetches the public OpenAPI spec; no secrets involved.
- name: Codegen check
run: python codegen/check.py
# Build, static checks, install the wheel in a fresh venv, smoke test it.
# The live contract stage needs FORM4API_TEST_KEY and runs locally before a release.
- name: Release check (offline)
run: python scripts/release_check.py --offline
9 changes: 8 additions & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- run: pip install build
# Gate: a failure in any of the next three steps stops the job before the
# publish step runs.
- name: Install with the dev extra
run: python -m pip install -e ".[dev]"
- name: Tests
run: python -m pytest
- name: Release check (offline)
run: python scripts/release_check.py --offline
- run: python -m build
- uses: pypa/gh-action-pypi-publish@release/v1
22 changes: 22 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
# Changelog

## Before publishing

Run the release check with a real API key and get it green BEFORE `twine upload`:

```
py -m pip install -e ".[dev]" # once: build, twine, check-wheel-contents, validator
$env:FORM4API_TEST_KEY = "<a Business-plan key>" # PowerShell; never paste it into a file
py scripts/release_check.py
```

It builds the sdist and wheel into a temp directory, runs `twine check --strict`
and `check-wheel-contents`, installs the wheel into a fresh venv, smoke-tests the
installed package from outside the repo, and compares live API responses with
the live OpenAPI spec (`contract/live-calls.json`). It never uploads anything.
`--offline` skips the live stage (CI uses it); `--keep` keeps the temp dirs.
Upload the files from the `dist/` you built with `py -m build`, only after a
passing run. Bump `pyproject.toml` and add the top entry below first: stage 1
fails if they disagree.

## Unreleased

- Added `form4api.__version__` (read from the installed package metadata, so it
always equals the pyproject version).

## 0.9.0 — 2026-10-05

- **Congress date quality (backend insiderapi #330/#331).** `CongressTradeDto`
Expand Down
11 changes: 11 additions & 0 deletions contract/live-calls.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{ "id": "stats", "method": "GET /v1/stats", "params": {} },
{ "id": "transactions-aapl", "method": "GET /v1/transactions", "params": { "ticker": "AAPL", "limit": 5 } },
{ "id": "filings-aapl", "method": "GET /v1/filings", "params": { "ticker": "AAPL", "limit": 5 } },
{ "id": "company-aapl", "method": "GET /v1/companies/{ticker}", "params": { "ticker": "AAPL" } },
{ "id": "company-insiders-aapl", "method": "GET /v1/companies/{ticker}/insiders", "params": { "ticker": "AAPL" } },
{ "id": "insider-search", "method": "GET /v1/insiders", "params": { "name": "cook" } },
{ "id": "signals", "method": "GET /v1/signals", "params": { "limit": 5 } },
{ "id": "congress-sony", "method": "GET /v1/congress/trades", "params": { "ticker": "SONY" } },
{ "id": "search-apple", "method": "GET /v1/search", "params": { "q": "apple" } }
]
4 changes: 4 additions & 0 deletions form4api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from form4api._client import AsyncForm4ApiClient, Form4ApiClient
# Single source of truth is pyproject.toml: _client reads the installed package
# metadata, so this cannot drift from the published version.
from form4api._client import _SDK_VERSION as __version__
from form4api._errors import (
AuthError,
Form4ApiError,
Expand All @@ -24,6 +27,7 @@
from form4api._webhook_utils import verify_webhook

__all__ = [
"__version__",
"Form4ApiClient",
"AsyncForm4ApiClient",
"Form4ApiError",
Expand Down
13 changes: 12 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,18 @@ Documentation = "https://www.form4api.com/docs"
Repository = "https://github.com/theodor90/form4api-py.git"

[project.optional-dependencies]
dev = ["pytest>=8", "pytest-httpx>=0.30", "respx>=0.21"]
# `contract` is what the live contract stage installs into its throwaway venv;
# `dev` is everything scripts/release_check.py and CI need on the host.
contract = ["openapi-schema-validator>=0.6.2"]
dev = [
"pytest>=8",
"pytest-httpx>=0.30",
"respx>=0.21",
"build>=1.2",
"twine>=5.1",
"check-wheel-contents>=0.6",
"openapi-schema-validator>=0.6.2",
]

[tool.setuptools.packages.find]
where = ["."]
Expand Down
Loading
Loading