fastapi-streams 0.0.1__tar.gz → 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/.github/workflows/ci.yml +10 -5
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/.github/workflows/compatibility.yml +9 -5
- fastapi_streams-0.1.0/.github/workflows/release.yml +115 -0
- fastapi_streams-0.1.0/CHANGELOG.md +88 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/PKG-INFO +8 -4
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/README.md +4 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/docs/README.md +1 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/docs/clients.md +52 -2
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/docs/codegen.md +38 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/docs/development.md +1 -0
- fastapi_streams-0.1.0/docs/releasing.md +160 -0
- fastapi_streams-0.1.0/examples/agent_runs/errors.py +37 -0
- fastapi_streams-0.1.0/examples/agent_runs/errors.ts +44 -0
- fastapi_streams-0.1.0/examples/agent_runs/generated/agent-runs/api.ts +89 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/generated/index.ts +1 -1
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/generated/transport/errors.ts +11 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/generated/types.ts +16 -16
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/generated_python/agent_runs/api.py +34 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/generated_python/models.py +4 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/__init__.py +1 -1
- fastapi_streams-0.1.0/fastapi_streams/codegen/diagnostics.py +79 -0
- fastapi_streams-0.1.0/fastapi_streams/codegen/documentation.py +59 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/enums.py +4 -4
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/ir.py +10 -1
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/python.py +35 -5
- {fastapi_streams-0.0.1/tests/fixtures/generated/transport → fastapi_streams-0.1.0/fastapi_streams/codegen/templates}/errors.ts +11 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/templates/python/api.py.j2 +5 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/templates/typescript/api.ts.j2 +9 -1
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/typescript.py +23 -11
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/pyproject.toml +6 -5
- fastapi_streams-0.1.0/scripts/check_release.py +69 -0
- fastapi_streams-0.1.0/tests/codegen/test_diagnostics.py +64 -0
- fastapi_streams-0.1.0/tests/codegen/test_documentation.py +50 -0
- fastapi_streams-0.1.0/tests/codegen/test_error_contracts.py +149 -0
- fastapi_streams-0.1.0/tests/codegen/test_literal_fields.py +103 -0
- fastapi_streams-0.1.0/tests/fixtures/generated/fixtures/api.ts +51 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/generated/index.ts +1 -1
- {fastapi_streams-0.0.1/fastapi_streams/codegen/templates → fastapi_streams-0.1.0/tests/fixtures/generated/transport}/errors.ts +11 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/generated/types.ts +16 -16
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/generated_python/fixtures/api.py +17 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/generated_python/models.py +2 -0
- fastapi_streams-0.1.0/tests/test_release.py +119 -0
- fastapi_streams-0.1.0/tests/typescript/errors.test.ts +69 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/typescript/tsconfig.json +1 -1
- fastapi_streams-0.0.1/examples/agent_runs/generated/agent-runs/api.ts +0 -49
- fastapi_streams-0.0.1/tests/fixtures/generated/fixtures/api.ts +0 -31
- fastapi_streams-0.0.1/uv.lock +0 -1218
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/.gitattributes +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/.gitignore +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/.pre-commit-config.yaml +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/AGENTS.md +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/LICENSE +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/docs/architecture.md +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/docs/quickstart.md +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/__init__.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/__init__.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/consume.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/consume.ts +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/generated/.fastapi-streams.json +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/generated/agent-runs/index.ts +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/generated/client.ts +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/generated/transport/sse.ts +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/generated_python/.fastapi-streams.json +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/generated_python/__init__.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/generated_python/agent_runs/__init__.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/generated_python/client.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/generated_python/py.typed +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/examples/agent_runs/generated_python/runtime.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/__init__.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/cli.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/names.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/openapi.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/schemas/LICENSE +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/schemas/README.md +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/schemas/openapi-3.2.json +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/templates/python/client.py.j2 +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/templates/python/runtime.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/templates/sse.ts +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/templates/typescript/client.ts.j2 +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/templating.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/codegen/writer.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/py.typed +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/fastapi_streams/router.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/package-lock.json +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/package.json +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/scripts/check_package.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/scripts/codegen.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/scripts/smoke_package.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/scripts/test_compat.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/__init__.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/codegen/helpers.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/codegen/test_additional_properties.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/codegen/test_codegen.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/codegen/test_contract_boundaries.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/codegen/test_discriminators.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/codegen/test_field_names.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/codegen/test_http_backends.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/codegen/test_intersections.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/codegen/test_nested_models.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/codegen/test_python_client.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/codegen/test_python_typing.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/codegen/test_required_properties.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/codegen/test_schema_matrix.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/contracts/test_openapi.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/README.md +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/__init__.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/agent_runs.openapi.json +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/compat-client.ts +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/generated/.fastapi-streams.json +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/generated/client.ts +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/generated/fixtures/index.ts +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/generated/transport/sse.ts +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/generated_python/.fastapi-streams.json +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/generated_python/__init__.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/generated_python/client.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/generated_python/fixtures/__init__.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/generated_python/py.typed +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/generated_python/runtime.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/integration_app.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/models.openapi.json +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/fixtures/models_app.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/integration/test_end_to_end.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/router/test_router.py +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/typescript/live-client.ts +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/typescript/models.test.ts +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/typescript/narrowing.ts +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/typescript/sse.test.ts +0 -0
- {fastapi_streams-0.0.1 → fastapi_streams-0.1.0}/tests/typing/client_usage.py +0 -0
|
@@ -2,7 +2,9 @@ name: CI
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
+
branches: ["**"]
|
|
5
6
|
pull_request:
|
|
7
|
+
workflow_call:
|
|
6
8
|
|
|
7
9
|
permissions:
|
|
8
10
|
contents: read
|
|
@@ -16,15 +18,17 @@ jobs:
|
|
|
16
18
|
matrix:
|
|
17
19
|
python: ["3.12", "3.13", "3.14"]
|
|
18
20
|
steps:
|
|
19
|
-
- uses: actions/checkout@v4
|
|
20
|
-
|
|
21
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
22
|
+
with:
|
|
23
|
+
persist-credentials: false
|
|
24
|
+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
21
25
|
with:
|
|
22
26
|
python-version: ${{ matrix.python }}
|
|
23
|
-
- uses: actions/setup-node@v4
|
|
27
|
+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
|
24
28
|
with:
|
|
25
29
|
node-version: "24"
|
|
26
30
|
cache: npm
|
|
27
|
-
- uses: astral-sh/setup-uv@v6
|
|
31
|
+
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
|
28
32
|
with:
|
|
29
33
|
version: "0.9.2"
|
|
30
34
|
enable-cache: true
|
|
@@ -38,7 +42,7 @@ jobs:
|
|
|
38
42
|
- run: npm run typecheck
|
|
39
43
|
- run: npm run test:coverage
|
|
40
44
|
- run: uv run pytest -q --cov --cov-report=term-missing --cov-report=xml:coverage-python.xml
|
|
41
|
-
- uses: actions/upload-artifact@v4
|
|
45
|
+
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
42
46
|
if: always()
|
|
43
47
|
with:
|
|
44
48
|
name: coverage-python-${{ matrix.python }}
|
|
@@ -47,4 +51,5 @@ jobs:
|
|
|
47
51
|
coverage-typescript.lcov
|
|
48
52
|
- run: uv run python scripts/codegen.py --check
|
|
49
53
|
- run: uv build
|
|
54
|
+
- run: uv run --frozen twine check --strict dist/*
|
|
50
55
|
- run: uv run python scripts/check_package.py
|
|
@@ -2,7 +2,9 @@ name: Dependency compatibility
|
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
|
+
branches: ['**']
|
|
5
6
|
pull_request:
|
|
7
|
+
workflow_call:
|
|
6
8
|
workflow_dispatch:
|
|
7
9
|
schedule:
|
|
8
10
|
- cron: '17 7 * * 1'
|
|
@@ -22,22 +24,24 @@ jobs:
|
|
|
22
24
|
python: ['3.12', '3.13', '3.14']
|
|
23
25
|
dependencies: [lowest, latest]
|
|
24
26
|
steps:
|
|
25
|
-
- uses: actions/checkout@v4
|
|
26
|
-
|
|
27
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
28
|
+
with:
|
|
29
|
+
persist-credentials: false
|
|
30
|
+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
27
31
|
with:
|
|
28
32
|
python-version: ${{ matrix.python }}
|
|
29
|
-
- uses: actions/setup-node@v4
|
|
33
|
+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
|
30
34
|
with:
|
|
31
35
|
node-version: '24'
|
|
32
36
|
cache: npm
|
|
33
|
-
- uses: astral-sh/setup-uv@v6
|
|
37
|
+
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
|
34
38
|
with:
|
|
35
39
|
version: '0.9.2'
|
|
36
40
|
enable-cache: true
|
|
37
41
|
- run: npm ci --ignore-scripts
|
|
38
42
|
- name: Resolve dependencies and run native FastAPI and client integration tests
|
|
39
43
|
run: python scripts/test_compat.py --python ${{ matrix.python }} --dependencies ${{ matrix.dependencies }}
|
|
40
|
-
- uses: actions/upload-artifact@v4
|
|
44
|
+
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
41
45
|
if: always()
|
|
42
46
|
with:
|
|
43
47
|
name: compatibility-${{ matrix.os }}-${{ matrix.python }}-${{ matrix.dependencies }}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags: ['v*']
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: release-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: false
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
validate:
|
|
16
|
+
runs-on: ubuntu-latest
|
|
17
|
+
timeout-minutes: 5
|
|
18
|
+
outputs:
|
|
19
|
+
version: ${{ steps.version.outputs.version }}
|
|
20
|
+
prerelease: ${{ steps.version.outputs.prerelease }}
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
23
|
+
with:
|
|
24
|
+
fetch-depth: 0
|
|
25
|
+
persist-credentials: false
|
|
26
|
+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
27
|
+
with:
|
|
28
|
+
python-version: '3.12'
|
|
29
|
+
- name: Validate tag, versions and main ancestry
|
|
30
|
+
id: version
|
|
31
|
+
env:
|
|
32
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
33
|
+
run: python scripts/check_release.py "$RELEASE_TAG" >> "$GITHUB_OUTPUT"
|
|
34
|
+
|
|
35
|
+
ci:
|
|
36
|
+
needs: validate
|
|
37
|
+
uses: ./.github/workflows/ci.yml
|
|
38
|
+
|
|
39
|
+
compatibility:
|
|
40
|
+
needs: validate
|
|
41
|
+
uses: ./.github/workflows/compatibility.yml
|
|
42
|
+
|
|
43
|
+
build:
|
|
44
|
+
needs: [validate, ci, compatibility]
|
|
45
|
+
runs-on: ubuntu-latest
|
|
46
|
+
timeout-minutes: 15
|
|
47
|
+
steps:
|
|
48
|
+
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
|
49
|
+
with:
|
|
50
|
+
persist-credentials: false
|
|
51
|
+
- uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5
|
|
52
|
+
with:
|
|
53
|
+
python-version: '3.12'
|
|
54
|
+
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
|
55
|
+
with:
|
|
56
|
+
node-version: '24'
|
|
57
|
+
cache: npm
|
|
58
|
+
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
|
|
59
|
+
with:
|
|
60
|
+
version: '0.9.2'
|
|
61
|
+
- run: uv sync --locked --all-extras
|
|
62
|
+
- run: npm ci --ignore-scripts
|
|
63
|
+
# Build the wheel from the sdist, including packaged templates and schemas.
|
|
64
|
+
- run: uv build --no-sources
|
|
65
|
+
- run: uv run --frozen twine check --strict dist/*
|
|
66
|
+
- run: uv run --frozen python scripts/check_package.py
|
|
67
|
+
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
|
68
|
+
with:
|
|
69
|
+
name: release-distributions
|
|
70
|
+
path: |
|
|
71
|
+
dist/*.whl
|
|
72
|
+
dist/*.tar.gz
|
|
73
|
+
if-no-files-found: error
|
|
74
|
+
retention-days: 30
|
|
75
|
+
|
|
76
|
+
publish:
|
|
77
|
+
needs: build
|
|
78
|
+
runs-on: ubuntu-latest
|
|
79
|
+
timeout-minutes: 10
|
|
80
|
+
environment:
|
|
81
|
+
name: pypi
|
|
82
|
+
url: https://pypi.org/p/fastapi-streams
|
|
83
|
+
permissions:
|
|
84
|
+
id-token: write
|
|
85
|
+
steps:
|
|
86
|
+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
87
|
+
with:
|
|
88
|
+
name: release-distributions
|
|
89
|
+
path: dist/
|
|
90
|
+
- name: Publish to PyPI with Trusted Publishing and attestations
|
|
91
|
+
uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # release/v1
|
|
92
|
+
with:
|
|
93
|
+
attestations: true
|
|
94
|
+
|
|
95
|
+
github-release:
|
|
96
|
+
needs: [validate, publish]
|
|
97
|
+
runs-on: ubuntu-latest
|
|
98
|
+
timeout-minutes: 5
|
|
99
|
+
permissions:
|
|
100
|
+
contents: write
|
|
101
|
+
steps:
|
|
102
|
+
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
|
103
|
+
with:
|
|
104
|
+
name: release-distributions
|
|
105
|
+
path: dist/
|
|
106
|
+
- name: Create GitHub Release with the published distributions
|
|
107
|
+
env:
|
|
108
|
+
GH_TOKEN: ${{ github.token }}
|
|
109
|
+
GH_REPO: ${{ github.repository }}
|
|
110
|
+
RELEASE_TAG: ${{ github.ref_name }}
|
|
111
|
+
PRERELEASE: ${{ needs.validate.outputs.prerelease }}
|
|
112
|
+
run: |
|
|
113
|
+
gh release create "$RELEASE_TAG" dist/* \
|
|
114
|
+
--verify-tag --generate-notes --title "$RELEASE_TAG" \
|
|
115
|
+
--prerelease="$PRERELEASE"
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes and consumer upgrade steps are recorded here. Entries marked
|
|
4
|
+
unreleased describe prepared source versions that have not been published.
|
|
5
|
+
|
|
6
|
+
## 0.1.0 - 2026-10-17
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
|
|
10
|
+
- Generated TypeScript JSDoc and Python method docstrings include OpenAPI
|
|
11
|
+
summaries, descriptions, deprecation, and SDK-specific iteration, cancellation,
|
|
12
|
+
validation, and error behavior. Endpoint descriptions remain the source for
|
|
13
|
+
business guarantees such as retention or job lifetime after disconnect.
|
|
14
|
+
- `OptionalDiscriminatorWarning` identifies optional discriminator fields in
|
|
15
|
+
stream event unions, including nested and referenced events. Diagnostics name
|
|
16
|
+
the operation, route, model, and field; generation preserves schema requiredness.
|
|
17
|
+
- Explicit `OperationErrorBody` aliases in both SDKs distinguish declared HTTP
|
|
18
|
+
response bodies from thrown exceptions. Existing `OperationError` aliases remain
|
|
19
|
+
available; TypeScript marks them deprecated. Method documentation lists the
|
|
20
|
+
declared HTTP statuses and media types. Existing Python anonymous error-model
|
|
21
|
+
imports such as `OperationError0` retain their names.
|
|
22
|
+
- TypeScript `isHttpError(error, isDetails)` narrows `HttpError.details` only after
|
|
23
|
+
a caller-supplied type guard succeeds. It adds no runtime dependencies or assumed
|
|
24
|
+
problem format. HTTP status and operation provenance remain caller checks.
|
|
25
|
+
- Checked TypeScript and Python error-handling consumer examples, plus a
|
|
26
|
+
standalone FastAPI dependency/problem-response regression without an error
|
|
27
|
+
framework. Public generated error-alias name collisions now fail explicitly.
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
|
|
31
|
+
- Develop in `mathisarends/canon`; package source, documentation, and issue URLs
|
|
32
|
+
now point to the monorepo.
|
|
33
|
+
- Single-value string fields now use TypeScript/Python literals instead of
|
|
34
|
+
synthesizing a separate public constant object or `StrEnum` for every field.
|
|
35
|
+
Explicitly named enums, multi-value field enums, and shared event discriminator
|
|
36
|
+
enums remain available. TypeScript also omits unnecessary property quotes and
|
|
37
|
+
union parentheses while preserving intersection grouping and extra-field types.
|
|
38
|
+
|
|
39
|
+
### Breaking generated-code changes
|
|
40
|
+
|
|
41
|
+
- Synthesized single-field constants such as `InvalidCredentialsProblemTitle`
|
|
42
|
+
and `InvalidCredentialsProblemType` are no longer exported. Replace usages like
|
|
43
|
+
`InvalidCredentialsProblemTitle.INVALID_CREDENTIALS` with the wire literal
|
|
44
|
+
`"Invalid credentials"`, then remove unused imports. Python singleton fields
|
|
45
|
+
now contain plain strings instead of generated `StrEnum` members; replace
|
|
46
|
+
identity checks with value comparisons. Shared discriminator values such as
|
|
47
|
+
`RunEventType.RUN_STARTED` and explicit schema enums are unchanged.
|
|
48
|
+
- Generated `OperationError`/`OperationErrorBody` names are reserved in both
|
|
49
|
+
SDKs. Python now reports collisions explicitly too. If a schema uses one of
|
|
50
|
+
these names, rename the colliding model or choose a distinct `operation_id`.
|
|
51
|
+
|
|
52
|
+
### Upgrade from 0.0.1
|
|
53
|
+
|
|
54
|
+
1. Update `fastapi-streams[codegen]` to `0.1.0`, using the same version for schema
|
|
55
|
+
export and both SDKs.
|
|
56
|
+
2. Regenerate committed clients from the application's OpenAPI document. Existing
|
|
57
|
+
method names and call signatures are unchanged:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
uv run fastapi-streams schema --app your_app:app --output openapi.json
|
|
61
|
+
uv run fastapi-streams generate --schema openapi.json --language typescript --output generated/typescript
|
|
62
|
+
uv run fastapi-streams generate --schema openapi.json --language python --output generated/python
|
|
63
|
+
uv run fastapi-streams check --app your_app:app --schema openapi.json --language typescript --output generated/typescript
|
|
64
|
+
uv run fastapi-streams check --app your_app:app --schema openapi.json --language python --output generated/python
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Substitute the application's existing module and output paths. Preserve
|
|
68
|
+
`--python-http-client httpx2` on generation and checks if it is already used.
|
|
69
|
+
3. Review optional discriminator warnings. For output-only Pydantic event models
|
|
70
|
+
whose defaults are always serialized, see the serialization configuration in
|
|
71
|
+
[the codegen guide](docs/codegen.md#event-discriminators). Do not make optional
|
|
72
|
+
request fields required merely because they have Python defaults. Add
|
|
73
|
+
endpoint-specific guarantees to OpenAPI descriptions. Export and regenerate
|
|
74
|
+
again after changing models or descriptions.
|
|
75
|
+
4. Apply the single-field constant migration above if the application imported
|
|
76
|
+
these generated names. HTTP payloads, method names, and signatures are unchanged.
|
|
77
|
+
5. Prefer `OperationErrorBody` over `OperationError` when referring to response
|
|
78
|
+
bodies. Keep caught errors unknown until checked; validate `HttpError.details`
|
|
79
|
+
with a body guard in TypeScript or Pydantic `TypeAdapter` in Python. See
|
|
80
|
+
[HTTP error contracts](docs/clients.md#http-error-contracts). Unexpected proxy
|
|
81
|
+
responses must continue through the generic `HttpError` fallback.
|
|
82
|
+
6. Run the consuming project's TypeScript/Python type checks and stream tests.
|
|
83
|
+
Commit the exported schema and regenerated SDKs together with call-site updates.
|
|
84
|
+
|
|
85
|
+
## 0.0.1
|
|
86
|
+
|
|
87
|
+
- Initial typed GET/POST JSON SSE routers, OpenAPI export, TypeScript and Python
|
|
88
|
+
generators, shared transports, and contract/client drift checks.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: fastapi-streams
|
|
3
|
-
Version: 0.0
|
|
3
|
+
Version: 0.1.0
|
|
4
4
|
Summary: Typed FastAPI streams with generated TypeScript and Python clients.
|
|
5
|
-
Project-URL: Documentation, https://github.com/mathisarends/
|
|
6
|
-
Project-URL: Source, https://github.com/mathisarends/fastapi-streams
|
|
7
|
-
Project-URL: Issues, https://github.com/mathisarends/
|
|
5
|
+
Project-URL: Documentation, https://github.com/mathisarends/canon/tree/main/packages/fastapi-streams/docs
|
|
6
|
+
Project-URL: Source, https://github.com/mathisarends/canon/tree/main/packages/fastapi-streams
|
|
7
|
+
Project-URL: Issues, https://github.com/mathisarends/canon/issues
|
|
8
8
|
Author: Mathis Arends
|
|
9
9
|
License-Expression: MIT
|
|
10
10
|
License-File: LICENSE
|
|
@@ -224,6 +224,9 @@ and committed OpenAPI regression fixtures in `tests/fixtures/`. Verify all gener
|
|
|
224
224
|
|
|
225
225
|
## Documentation
|
|
226
226
|
|
|
227
|
+
See the [changelog and upgrade steps](CHANGELOG.md) when updating the library
|
|
228
|
+
and regenerating committed clients.
|
|
229
|
+
|
|
227
230
|
| Guide | Topics |
|
|
228
231
|
| --- | --- |
|
|
229
232
|
| [Quickstart](docs/quickstart.md) | Setup, event models, and FastAPI integration. |
|
|
@@ -231,6 +234,7 @@ and committed OpenAPI regression fixtures in `tests/fixtures/`. Verify all gener
|
|
|
231
234
|
| [Client usage](docs/clients.md) | Enums, authentication, cancellation, and errors. |
|
|
232
235
|
| [Architecture](docs/architecture.md) | Native SSE, shared IR, templates, and transports. |
|
|
233
236
|
| [Development](docs/development.md) | Tests, supported scope, and roadmap. |
|
|
237
|
+
| [Releasing](docs/releasing.md) | Tag-based releases and credential-free PyPI publishing. |
|
|
234
238
|
|
|
235
239
|
v0.0.1 supports GET/POST JSON SSE streams. Ordinary REST endpoints stay in the
|
|
236
240
|
contract for your existing REST SDK. See the [supported scope](docs/development.md#supported-scope-and-roadmap).
|
|
@@ -206,6 +206,9 @@ and committed OpenAPI regression fixtures in `tests/fixtures/`. Verify all gener
|
|
|
206
206
|
|
|
207
207
|
## Documentation
|
|
208
208
|
|
|
209
|
+
See the [changelog and upgrade steps](CHANGELOG.md) when updating the library
|
|
210
|
+
and regenerating committed clients.
|
|
211
|
+
|
|
209
212
|
| Guide | Topics |
|
|
210
213
|
| --- | --- |
|
|
211
214
|
| [Quickstart](docs/quickstart.md) | Setup, event models, and FastAPI integration. |
|
|
@@ -213,6 +216,7 @@ and committed OpenAPI regression fixtures in `tests/fixtures/`. Verify all gener
|
|
|
213
216
|
| [Client usage](docs/clients.md) | Enums, authentication, cancellation, and errors. |
|
|
214
217
|
| [Architecture](docs/architecture.md) | Native SSE, shared IR, templates, and transports. |
|
|
215
218
|
| [Development](docs/development.md) | Tests, supported scope, and roadmap. |
|
|
219
|
+
| [Releasing](docs/releasing.md) | Tag-based releases and credential-free PyPI publishing. |
|
|
216
220
|
|
|
217
221
|
v0.0.1 supports GET/POST JSON SSE streams. Ordinary REST endpoints stay in the
|
|
218
222
|
contract for your existing REST SDK. See the [supported scope](docs/development.md#supported-scope-and-roadmap).
|
|
@@ -9,6 +9,7 @@ TypeScript Fetch and Python HTTPX clients from the same definitions.
|
|
|
9
9
|
| [Contracts and codegen](codegen.md) | Export OpenAPI, generate both clients, and detect drift in CI. |
|
|
10
10
|
| [Client usage](clients.md) | Named event values, typed iteration, authentication, cancellation, and errors. |
|
|
11
11
|
| [Development](development.md) | Local checks, test coverage, supported features, and roadmap. |
|
|
12
|
+
| [Releasing](releasing.md) | Tag-based releases, PyPI Trusted Publishing setup, and recovery. |
|
|
12
13
|
| [Architecture](architecture.md) | Server integration, shared IR, generation, and transport boundaries. |
|
|
13
14
|
|
|
14
15
|
Start with the [quickstart](quickstart.md), then use the
|
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
## Named values and typed iteration
|
|
6
6
|
|
|
7
7
|
Discriminated unions produce an enum named after the union and discriminator:
|
|
8
|
-
`RunEvent` + `type` becomes `RunEventType`.
|
|
9
|
-
also get named values.
|
|
8
|
+
`RunEvent` + `type` becomes `RunEventType`. Named string enums and multi-value
|
|
9
|
+
literal model fields also get named values. Single constant fields, such as
|
|
10
|
+
problem titles and URIs, remain inline literals. Python emits `StrEnum` for enums;
|
|
11
|
+
TypeScript emits an `as const`
|
|
10
12
|
object plus a union type. Both retain exhaustive discriminator narrowing.
|
|
11
13
|
Parameters use camelCase in TypeScript and snake_case in Python; wire aliases
|
|
12
14
|
are preserved.
|
|
@@ -117,6 +119,54 @@ Pydantic; TypeScript provides static types and JSON decoding, with no runtime
|
|
|
117
119
|
schema validator. Cancellation propagates normally; application error events
|
|
118
120
|
remain ordinary members of the user-defined union.
|
|
119
121
|
|
|
122
|
+
## HTTP error contracts
|
|
123
|
+
|
|
124
|
+
The SDK works with ordinary OpenAPI response declarations, including
|
|
125
|
+
`application/json`, `application/problem+json`, and other declared response media
|
|
126
|
+
types. No error registry or particular problem envelope is required. Declare
|
|
127
|
+
HTTP failures with FastAPI's `responses=`; put authentication, authorization,
|
|
128
|
+
and resource checks in dependencies so they can fail before the response starts.
|
|
129
|
+
Even generator statements before the first `yield` execute after response start.
|
|
130
|
+
|
|
131
|
+
Each operation with declared error schemas exports an `OperationErrorBody`
|
|
132
|
+
union: for example `StreamRunEventsErrorBody`. The earlier `OperationError`
|
|
133
|
+
name remains an alias for compatibility; it describes response bodies, not
|
|
134
|
+
the exceptions thrown by the SDK. Python imports these aliases from `models`;
|
|
135
|
+
TypeScript exports them from the SDK entrypoint.
|
|
136
|
+
|
|
137
|
+
Catch HTTP failures around **iteration**, since opening the request is lazy.
|
|
138
|
+
`HttpError.details` deliberately remains unvalidated: proxies and servers can
|
|
139
|
+
return unexpected JSON, plain text, HTML, incomplete bodies, or no body at all.
|
|
140
|
+
An operation's body union does not associate each variant with a status and does
|
|
141
|
+
not prove which operation produced an exception. Check the actual HTTP status
|
|
142
|
+
and validate the body before using application fields.
|
|
143
|
+
|
|
144
|
+
TypeScript provides `isHttpError(error, isDetails)`. Supply a type guard that
|
|
145
|
+
checks every field promised by its return type, or adapt an existing runtime
|
|
146
|
+
validator. The helper verifies the SDK exception class and invokes that guard;
|
|
147
|
+
it does not generate a schema validator or validate the HTTP status. A generic
|
|
148
|
+
type argument alone cannot replace the required guard. Failed guards leave the
|
|
149
|
+
original `HttpError` available for generic handling.
|
|
150
|
+
|
|
151
|
+
Python can validate `error.details` with `TypeAdapter(OperationErrorBody)` and
|
|
152
|
+
handle `ValidationError` as an unexpected response. Both approaches retain
|
|
153
|
+
the original status, response, and diagnostics. Schema unions with permissive
|
|
154
|
+
variants may accept a broad range of bodies; inspect the parsed variant before
|
|
155
|
+
using its fields. Existing limits on bounded HTTP diagnostics still apply.
|
|
156
|
+
|
|
157
|
+
The checked consumer examples show HTTP validation errors, fallback handling,
|
|
158
|
+
stream parsing/transport errors, and ordinary application error events:
|
|
159
|
+
|
|
160
|
+
- [TypeScript error handling](../examples/agent_runs/errors.ts), compiled with the
|
|
161
|
+
generated SDK under the repository's strict TypeScript configuration.
|
|
162
|
+
- [Python error handling](../examples/agent_runs/errors.py), checked by Pyright
|
|
163
|
+
with the generated models.
|
|
164
|
+
|
|
165
|
+
Caller cancellation continues to propagate. Neither SDK converts application
|
|
166
|
+
error events into exceptions or automatically retries a stream.
|
|
167
|
+
|
|
168
|
+
## Python model fields
|
|
169
|
+
|
|
120
170
|
Python uses `None` for omitted optional fields; `model_fields_set` distinguishes
|
|
121
171
|
absence from explicit null. Explicit null is rejected for fields that may only
|
|
122
172
|
be omitted. Requests exclude unset fields and serialize using wire aliases.
|
|
@@ -29,6 +29,12 @@ The generator extracts application data from
|
|
|
29
29
|
reachable from their parameters, bodies, events and error responses are emitted.
|
|
30
30
|
Operations use the first tag as their group, defaulting to `streams`.
|
|
31
31
|
|
|
32
|
+
Generated method documentation carries each operation's `summary`, `description`,
|
|
33
|
+
and `deprecated` marker alongside language-specific SDK behavior. Describe
|
|
34
|
+
replay, retention, and server job lifetime explicitly on your endpoint; the
|
|
35
|
+
generator does not infer these business guarantees. Python early exits should
|
|
36
|
+
use `contextlib.aclosing`; TypeScript `for await` cleanup closes its reader.
|
|
37
|
+
|
|
32
38
|
TypeScript has no runtime dependencies and uses Fetch-compatible APIs. The
|
|
33
39
|
example uses ES modules and is tested on Node 24. Generated Python packages
|
|
34
40
|
require Python 3.12+, `httpx>=0.28` and `pydantic>=2.11.2` (`uv add httpx pydantic`).
|
|
@@ -57,6 +63,38 @@ REST operations remain available in the contract for an existing SDK generator
|
|
|
57
63
|
such as Orval or HeyAPI. Keep REST and streaming SDKs in separate output
|
|
58
64
|
directories. Reusing external generated model imports is a future feature.
|
|
59
65
|
|
|
66
|
+
## Event discriminators
|
|
67
|
+
|
|
68
|
+
A Python default allows omitting a field when constructing a model. It does not
|
|
69
|
+
automatically make the field required in JSON Schema. The generator respects
|
|
70
|
+
`required` and warns with `OptionalDiscriminatorWarning` when a stream event
|
|
71
|
+
union uses an optional discriminator, including nested unions. An absent tag may
|
|
72
|
+
prevent variant selection; TypeScript's static types do not validate incoming JSON.
|
|
73
|
+
|
|
74
|
+
For **output-only** event models whose serializer always emits default fields,
|
|
75
|
+
configure the output schema to describe that behavior:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from pydantic import BaseModel, ConfigDict
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class StreamEvent(BaseModel):
|
|
82
|
+
model_config = ConfigDict(
|
|
83
|
+
json_schema_mode_override="serialization",
|
|
84
|
+
json_schema_serialization_defaults_required=True,
|
|
85
|
+
)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Inherit event variants from this model, or apply the configuration to an existing
|
|
89
|
+
output-only base. It marks all serialized default fields required, not only the
|
|
90
|
+
discriminator. Avoid applying it globally to shared request models: input and
|
|
91
|
+
output requiredness can differ. Alternatively, declare the tag without a default
|
|
92
|
+
and pass it explicitly. Regenerate the schema and clients after changing models.
|
|
93
|
+
|
|
94
|
+
Warnings are advisory and do not change fields or defaults. Python callers can
|
|
95
|
+
filter or promote this category using the standard `warnings` module; the category
|
|
96
|
+
is available from `fastapi_streams.codegen.diagnostics`.
|
|
97
|
+
|
|
60
98
|
## Check drift in CI
|
|
61
99
|
|
|
62
100
|
```sh
|
|
@@ -17,6 +17,7 @@ npm run test:coverage
|
|
|
17
17
|
uv run pytest -q --cov --cov-report=term-missing --cov-report=xml:coverage-python.xml
|
|
18
18
|
uv run python scripts/codegen.py --check
|
|
19
19
|
uv build
|
|
20
|
+
uv run --frozen twine check --strict dist/*
|
|
20
21
|
uv run python scripts/check_package.py
|
|
21
22
|
```
|
|
22
23
|
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Releasing
|
|
2
|
+
|
|
3
|
+
[Documentation](README.md) · [Project](../README.md)
|
|
4
|
+
|
|
5
|
+
A pushed Git tag is the release signal. The agent preparing a release only needs
|
|
6
|
+
permission to push the release tag to GitHub. GitHub Actions authenticates to PyPI
|
|
7
|
+
with OIDC (Trusted Publishing); no PyPI password or API token is stored in GitHub
|
|
8
|
+
or provided to an agent.
|
|
9
|
+
|
|
10
|
+
## One-time maintainer setup (required before the first tag)
|
|
11
|
+
|
|
12
|
+
### 1. Configure the GitHub environment
|
|
13
|
+
|
|
14
|
+
Open [Settings → Environments](https://github.com/mathisarends/fastapi-streams/settings/environments)
|
|
15
|
+
and create an environment named **`pypi`**.
|
|
16
|
+
|
|
17
|
+
- Under **Deployment branches and tags**, select **Selected branches and tags**
|
|
18
|
+
and add a **tag** rule for `v*`. Do not add a branch rule.
|
|
19
|
+
- Recommended: add a required reviewer if you want a human to approve each PyPI
|
|
20
|
+
upload after the agent pushes a tag. Enable prevention of self-review if your
|
|
21
|
+
approval policy requires another person. Without required reviewers, a valid
|
|
22
|
+
tag publishes automatically after validation and tests pass.
|
|
23
|
+
- Do not add a PyPI secret. The `publish` job requests `id-token: write`; the
|
|
24
|
+
separate GitHub Release job requests `contents: write` for its built-in
|
|
25
|
+
`GITHUB_TOKEN`. Repository or organization policies must allow these permissions
|
|
26
|
+
and the actions referenced in the workflows.
|
|
27
|
+
|
|
28
|
+
Create and protect this environment explicitly before publishing; a workflow can
|
|
29
|
+
otherwise create an environment without your intended protection rules.
|
|
30
|
+
|
|
31
|
+
### 2. Register the PyPI Trusted Publisher
|
|
32
|
+
|
|
33
|
+
Sign in to PyPI with the account that owns (or will own) `fastapi-streams`.
|
|
34
|
+
For an existing project, open its
|
|
35
|
+
[Publishing settings](https://pypi.org/manage/project/fastapi-streams/settings/publishing/).
|
|
36
|
+
For a first publication, use
|
|
37
|
+
[Add a new pending publisher](https://pypi.org/manage/account/publishing/).
|
|
38
|
+
|
|
39
|
+
Select **GitHub** and enter these exact values:
|
|
40
|
+
|
|
41
|
+
| Field | Value |
|
|
42
|
+
| --- | --- |
|
|
43
|
+
| PyPI project name (pending publishers only) | `fastapi-streams` |
|
|
44
|
+
| Owner | `mathisarends` |
|
|
45
|
+
| Repository name | `fastapi-streams` |
|
|
46
|
+
| Workflow name | `release.yml` (filename only) |
|
|
47
|
+
| Environment name | `pypi` |
|
|
48
|
+
|
|
49
|
+
Confirm you control the package name. A pending publisher does not reserve a name;
|
|
50
|
+
the project is created on the first successful upload. PyPI and TestPyPI have
|
|
51
|
+
independent accounts and publisher registrations; this workflow publishes to
|
|
52
|
+
production PyPI only.
|
|
53
|
+
|
|
54
|
+
### 3. Protect the release inputs
|
|
55
|
+
|
|
56
|
+
In [Settings → Rules → Rulesets](https://github.com/mathisarends/fastapi-streams/settings/rules),
|
|
57
|
+
protect `main` with pull request reviews and the existing CI and compatibility
|
|
58
|
+
checks. Review changes to the workflows, packaging configuration, and release
|
|
59
|
+
scripts as carefully as library changes.
|
|
60
|
+
|
|
61
|
+
Create a tag ruleset targeting `v*`. Restrict creation to authorized release
|
|
62
|
+
maintainers or a dedicated release GitHub App, and prevent updates and deletion.
|
|
63
|
+
Configure bypass access deliberately so the actor pushing a release tag can
|
|
64
|
+
create it. An agent that only prepares release PRs does not need tag creation
|
|
65
|
+
permission. An agent authorized to trigger releases does need that GitHub
|
|
66
|
+
permission, but still needs no PyPI credentials.
|
|
67
|
+
|
|
68
|
+
## Prepare and trigger a release
|
|
69
|
+
|
|
70
|
+
1. Resolve known release blockers and decide which changes belong in this version.
|
|
71
|
+
2. Prepare a PR against `main`. Update `project.version` in `pyproject.toml` and
|
|
72
|
+
`__version__` in `fastapi_streams/__init__.py`, then run `uv lock` and commit the
|
|
73
|
+
updated `uv.lock`. Keep all three versions identical. For the first `0.0.1`
|
|
74
|
+
release these files already agree; a version bump is only needed for a new
|
|
75
|
+
version. Describe user-facing changes in the PR; GitHub uses merged PRs to
|
|
76
|
+
generate release notes.
|
|
77
|
+
3. Merge after review and passing checks. Then tag the intended commit on `main`:
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
git fetch origin main --tags
|
|
81
|
+
git switch main
|
|
82
|
+
git pull --ff-only origin main
|
|
83
|
+
python scripts/check_release.py v0.0.1
|
|
84
|
+
git tag -a v0.0.1 -m "Release v0.0.1"
|
|
85
|
+
git push origin v0.0.1
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Substitute the intended version everywhere. For an older release candidate
|
|
89
|
+
already on `main`, check out that exact commit before validating and tagging.
|
|
90
|
+
An annotated tag is recommended; do not move or reuse a published tag.
|
|
91
|
+
|
|
92
|
+
4. Follow the [Release workflow](https://github.com/mathisarends/fastapi-streams/actions/workflows/release.yml)
|
|
93
|
+
and approve the `pypi` deployment if required reviewers are configured.
|
|
94
|
+
5. Verify the version on [PyPI](https://pypi.org/project/fastapi-streams/) and the
|
|
95
|
+
[GitHub Releases page](https://github.com/mathisarends/fastapi-streams/releases).
|
|
96
|
+
Consumers can then install `fastapi-streams[codegen]==0.0.1` from PyPI.
|
|
97
|
+
|
|
98
|
+
Supported tags are `vX.Y.Z`, `vX.Y.ZaN`, `vX.Y.ZbN`, and `vX.Y.ZrcN`, using
|
|
99
|
+
canonical numbers without leading zeroes. Prereleases are published to PyPI and
|
|
100
|
+
marked as prereleases on GitHub. Development, local, and post-release versions
|
|
101
|
+
are deliberately rejected.
|
|
102
|
+
|
|
103
|
+
If a GitHub Actions agent pushes the tag, use an appropriately scoped GitHub App
|
|
104
|
+
installation token or personal access token. Events caused by the repository's
|
|
105
|
+
default `GITHUB_TOKEN` do not normally start another workflow. A locally running
|
|
106
|
+
agent can use its authorized GitHub Git credentials. Neither approach requires
|
|
107
|
+
PyPI credentials.
|
|
108
|
+
|
|
109
|
+
## What the workflow guarantees
|
|
110
|
+
|
|
111
|
+
`.github/workflows/release.yml` runs these stages in order:
|
|
112
|
+
|
|
113
|
+
1. **Validate:** reject unsupported tags, mismatched versions in the project,
|
|
114
|
+
module or lockfile, and commits outside `origin/main`. Validation reads the
|
|
115
|
+
module with Python's AST parser instead of importing package code.
|
|
116
|
+
2. **Test:** call the existing CI workflow (Python 3.12–3.14, lint, types, Python
|
|
117
|
+
and TypeScript coverage, generated-file drift and wheel smoke tests) and the
|
|
118
|
+
complete Linux/Windows dependency compatibility matrix. These run against the
|
|
119
|
+
tag's exact commit and must all pass.
|
|
120
|
+
3. **Build:** check the lockfile, build an sdist and a wheel from it, check metadata
|
|
121
|
+
and the README with Twine, then smoke-test this wheel in isolated environments
|
|
122
|
+
for the core install and each Python HTTP backend. Upload the distributions as
|
|
123
|
+
one immutable workflow artifact.
|
|
124
|
+
4. **Publish:** download that artifact in a fresh job, pass the `pypi` environment
|
|
125
|
+
restrictions, and publish through PyPI Trusted Publishing with attestations.
|
|
126
|
+
This job has no source checkout, build steps, or stored credentials. Only this
|
|
127
|
+
job can request an OIDC token.
|
|
128
|
+
5. **GitHub Release:** after PyPI succeeds, attach the same wheel and sdist to a
|
|
129
|
+
GitHub Release with automatically generated release notes. Only this job has
|
|
130
|
+
repository write permission.
|
|
131
|
+
|
|
132
|
+
Runs for the same tag are serialized without canceling an in-progress release.
|
|
133
|
+
All external actions in the release and its called workflows are pinned to full
|
|
134
|
+
commit SHAs; review and update those pins when adopting action updates.
|
|
135
|
+
|
|
136
|
+
## Failures and retries
|
|
137
|
+
|
|
138
|
+
- Validation, test or build failure: nothing is published. Fix the release inputs
|
|
139
|
+
through a PR and use a new version/tag for the corrected candidate.
|
|
140
|
+
- Missing publisher or environment approval: complete the setup above and rerun
|
|
141
|
+
**failed jobs** on the original workflow run. This preserves the tested build
|
|
142
|
+
artifact. Artifacts are retained for 30 days.
|
|
143
|
+
- PyPI succeeded but GitHub Release creation failed: rerun only the failed GitHub
|
|
144
|
+
Release job. Do not rerun successful publishing jobs or rebuild the same PyPI
|
|
145
|
+
version. If a GitHub Release already exists after a partial failure, inspect it
|
|
146
|
+
and attach the original run's missing assets manually.
|
|
147
|
+
- Partial PyPI upload: inspect which files reached PyPI before retrying. The
|
|
148
|
+
workflow deliberately does not skip existing files; PyPI does not allow replacing
|
|
149
|
+
a published distribution. Publish a new version for changed package contents.
|
|
150
|
+
|
|
151
|
+
The setup is implemented here without creating a tag or publishing a package.
|
|
152
|
+
The first live upload also verifies your external PyPI and environment settings.
|
|
153
|
+
|
|
154
|
+
## References
|
|
155
|
+
|
|
156
|
+
- [PyPI: publishing with a Trusted Publisher](https://docs.pypi.org/trusted-publishers/using-a-publisher/)
|
|
157
|
+
- [PyPI: creating a project with a pending publisher](https://docs.pypi.org/trusted-publishers/creating-a-project-through-oidc/)
|
|
158
|
+
- [PyPI: security model](https://docs.pypi.org/trusted-publishers/security-model/)
|
|
159
|
+
- [GitHub: OIDC with PyPI](https://docs.github.com/en/actions/how-tos/secure-your-work/security-harden-deployments/oidc-in-pypi)
|
|
160
|
+
- [GitHub: triggering workflows from workflows](https://docs.github.com/en/actions/how-tos/writing-workflows/choosing-when-your-workflow-runs/triggering-a-workflow)
|