Skip to content

Commit 13b2f85

Browse files
committed
feat!: rebuild the client as 2.0.0 against API 2.26.0
Replaces the 1.x client, generated from Swagger Codegen against a 2020-era specification, with one regenerated from api-reference v2.26.0 through a pipeline that reproduces from this repository alone. 123 operations, the full public surface of API 2.26.0, against 119 before. `FlatClient` and `AsyncFlatClient`: the generator emits sync or async and never both, so tools/generate.sh runs it twice and merges the async client under `flat_api.aio`. Typed errors, a retry layer that understands Flat's rate limiting (HTTP 403 with the reset in `X-RateLimit-Reset`, not 429), cursor pagination that reads the undeclared `Link` header, `OAuth2Helper`, and a `py.typed` marker so a consumer's type checker stops ignoring the annotations. `SPEC_REF=v2.26.0 ./tools/generate.sh` fetches the specification by tag, runs openapi-generator pinned to an exact 7.24.0, wipes the zone `.sdkgen.yaml` declares generated, applies the post-generation patches and writes OPERATIONS.json. Two checks guard it: determinism, that the same inputs twice produce identical trees, and idempotency, that re-applying every patch changes nothing. Packaging was broken in two ways that a green build hid. `uv build` emitted `flat_api-0.0.0` and exited 0, because pyproject.toml still carried a `[tool.poetry]` table under a setuptools backend, so setuptools saw no `[project]` table and fell back to directory discovery for the name and 0.0.0 for the version; the only guard compared the tag against `cat VERSION`, which the build never read. And the dependency list omitted `httpx`, `aiohttp` and `aiohttp-retry`, all of which `flat_api/__init__` reaches through its own re-exports, so a clean install could not `import flat_api` at all. It is now a PEP 621 project with the version derived from VERSION, Apache-2.0 declared with its text, and `requires-python >=3.11` matching the tested matrix. The release-please workflows are removed. They published on every push to master with a long-lived token, taking their version from release-please's manifest rather than VERSION, which bypassed the merge gate, the tag, OIDC and provenance. Publishing is now the tag alone: tag-on-merge.yml creates it with SDK_RELEASE_TOKEN, since GitHub does not start a workflow run for an event created with the automatic GITHUB_TOKEN, and release.yml uploads to PyPI by trusted publishing with attestations. Verified: import and compileall on 3.11, 3.12 and 3.13; a clean install of the built wheel imports FlatClient, AsyncFlatClient and OAuth2Helper; the wheel carries py.typed, flat_api/aio/ and LICENSE and no tests or docs; 123 of 123 operations and 23 scopes with no internal leak; 100% documentation coverage; determinism, idempotency and zone checks pass; and all three LTI 1.3 modes deserialize to their own variant.
1 parent df91280 commit 13b2f85

856 files changed

Lines changed: 95651 additions & 15447 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.DS_Store‎

8 KB
Binary file not shown.

‎.github/workflows/ci.yml‎

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: CI
2+
3+
# Uncredentialed checks only. The production smoke test holds @tests.flat.io credentials and runs
4+
# in GitLab, reporting back as a required commit status, so those secrets never reach a public log
5+
# (FR-007g, FR-007h).
6+
7+
on:
8+
pull_request:
9+
push:
10+
branches: [master, main]
11+
12+
permissions:
13+
contents: read
14+
15+
concurrency:
16+
group: ${{ github.workflow }}-${{ github.ref }}
17+
cancel-in-progress: true
18+
19+
jobs:
20+
build:
21+
name: build (python ${{ matrix.python }})
22+
runs-on: ubuntu-latest
23+
strategy:
24+
fail-fast: false
25+
matrix:
26+
python: ['3.11', '3.12', '3.13']
27+
steps:
28+
- uses: actions/checkout@v4
29+
- uses: astral-sh/setup-uv@v5
30+
with:
31+
python-version: ${{ matrix.python }}
32+
- run: uv pip install --system -r requirements.txt
33+
- name: Import the package
34+
run: python -c "import flat_api; print(flat_api.__version__)"
35+
- name: Compile every module
36+
run: python -m compileall -q flat_api
37+
38+
checks:
39+
name: coverage, leak, docs, determinism, idempotency
40+
runs-on: ubuntu-latest
41+
steps:
42+
- uses: actions/checkout@v4
43+
- uses: actions/checkout@v4
44+
with:
45+
repository: FlatIO/api-reference
46+
ref: ${{ vars.SPEC_REF || 'v2.25.1' }}
47+
path: .api-reference
48+
- uses: astral-sh/setup-uv@v5
49+
- name: Fetch the pipeline checker
50+
run: |
51+
git clone --depth 1 https://${{ secrets.GITLAB_READ_TOKEN }}@git.flat.io/flat/api-client-gen.git .pipeline
52+
- name: Validate the manifest
53+
run: cd .pipeline && uv run sdk-validate-manifest ../.sdkgen.yaml
54+
- name: The specification must not contain a shape generators mishandle
55+
run: cd .pipeline && uv run sdk-lint-spec --spec ../.api-reference/spec/openapi.yaml
56+
- name: Coverage and internal-leak (FR-001, FR-002)
57+
run: cd .pipeline && uv run sdk-check --spec ../.api-reference/spec/openapi.yaml --inventory ../OPERATIONS.json
58+
- name: Documentation coverage (FR-004)
59+
run: cd .pipeline && uv run sdk-check-docs --spec ../.api-reference/spec/openapi.yaml --inventory ../OPERATIONS.json
60+
- name: Determinism (FR-024)
61+
run: .pipeline/checker/check_determinism.sh . .api-reference/spec/openapi.yaml
62+
- name: Idempotency (FR-025)
63+
run: .pipeline/checker/check_idempotency.sh .

‎.github/workflows/python-push.yml‎

Lines changed: 0 additions & 39 deletions
This file was deleted.

‎.github/workflows/python.yml‎

Lines changed: 0 additions & 38 deletions
This file was deleted.

‎.github/workflows/release.yml‎

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
name: Release
2+
3+
# Publishing is triggered by a version tag and nothing else (FR-019a). The tag is created by
4+
# tag-on-merge.yml, so the approval -> merge -> tag -> publish ordering is enforced by the
5+
# platform rather than by convention (FR-007f).
6+
7+
on:
8+
push:
9+
tags: ['[0-9]+.[0-9]+.[0-9]+']
10+
11+
permissions:
12+
contents: read
13+
id-token: write # OIDC for PyPI trusted publishing and provenance (FR-021a, FR-021b)
14+
15+
jobs:
16+
publish:
17+
runs-on: ubuntu-latest
18+
environment: pypi
19+
steps:
20+
- uses: actions/checkout@v4
21+
- uses: astral-sh/setup-uv@v5
22+
with:
23+
python-version: '3.12'
24+
25+
- name: The tag must match the packaged version
26+
run: |
27+
TAG="${GITHUB_REF_NAME}"
28+
PKG="$(cat VERSION)"
29+
[ "$TAG" = "$PKG" ] || { echo "tag $TAG != VERSION $PKG"; exit 1; }
30+
31+
- run: uv build
32+
33+
# Assert the artifact, not just the file. setuptools derives the version from VERSION, but a
34+
# broken pyproject.toml still builds: it silently produced flat_api-0.0.0 once. PyPI never
35+
# lets a version be replaced, so this has to fail here rather than after the upload.
36+
- name: The built artifact must carry the tagged version
37+
run: |
38+
TAG="${GITHUB_REF_NAME}"
39+
test -f "dist/flat_api-${TAG}-py3-none-any.whl" || {
40+
echo "no wheel for ${TAG}; dist/ holds:"; ls dist/; exit 1;
41+
}
42+
test -f "dist/flat_api-${TAG}.tar.gz" || {
43+
echo "no sdist for ${TAG}; dist/ holds:"; ls dist/; exit 1;
44+
}
45+
46+
# No long-lived token exists: PyPI trusts this repository and workflow by OIDC.
47+
- name: Publish to PyPI (trusted publishing)
48+
uses: pypa/gh-action-pypi-publish@release/v1
49+
with:
50+
attestations: true

‎.github/workflows/tag-on-merge.yml‎

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: Tag on merge
2+
3+
# Merging is what creates the version tag, and the tag is what publishes (FR-007f, FR-019a).
4+
# A breaking release waits for a human to merge, which is how the FR-019 approval is expressed.
5+
6+
on:
7+
push:
8+
branches: [master, main]
9+
10+
permissions:
11+
contents: write
12+
13+
jobs:
14+
tag:
15+
runs-on: ubuntu-latest
16+
steps:
17+
# SDK_RELEASE_TOKEN, not the automatic GITHUB_TOKEN. GitHub does not start a workflow run for
18+
# an event created with GITHUB_TOKEN, so a tag pushed with it would never trigger release.yml
19+
# and nothing would ever publish. That failure is silent: the tag appears, the release
20+
# workflow simply never runs. Needs contents:write on this repository.
21+
- uses: actions/checkout@v4
22+
with:
23+
fetch-depth: 0
24+
token: ${{ secrets.SDK_RELEASE_TOKEN }}
25+
- name: Tag the version if it is new
26+
run: |
27+
test -n "${{ secrets.SDK_RELEASE_TOKEN }}" || {
28+
echo "SDK_RELEASE_TOKEN is not set: the tag would not trigger release.yml"; exit 1;
29+
}
30+
VERSION="$(cat VERSION)"
31+
if git rev-parse "$VERSION" >/dev/null 2>&1; then
32+
echo "Tag $VERSION already exists, nothing to do."
33+
exit 0
34+
fi
35+
git config user.name "Flat SDK bot"
36+
git config user.email "developers@flat.io"
37+
git tag -a "$VERSION" -m "Release $VERSION"
38+
git push origin "$VERSION"

‎.gitignore‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,12 @@ target/
6565

6666
# Virtualenv
6767
.venv
68+
69+
# generation scratch tree (tools/generate.sh)
70+
.sdkgen-scratch/
71+
.openapi-spec.yaml
72+
73+
# build and tooling artifacts
74+
.venv/
75+
.pytest_cache/
76+
.coverage

0 commit comments

Comments
 (0)