fastapi-router-versioning 1.0.0__tar.gz → 1.0.2__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.
Files changed (50) hide show
  1. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/.github/labeler.yml +1 -1
  2. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/.github/workflows/bump-pre-commit-hooks.yml +5 -5
  3. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/.github/workflows/create-draft-release.yml +5 -5
  4. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/.github/workflows/detect-conflicts.yml +1 -1
  5. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/.github/workflows/guard-dependencies.yml +1 -1
  6. fastapi_router_versioning-1.0.2/.github/workflows/labeler.yml +20 -0
  7. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/.github/workflows/pre-commit.yml +4 -4
  8. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/.github/workflows/prepare-release.yml +6 -6
  9. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/.github/workflows/publish.yml +3 -3
  10. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/.github/workflows/test-redistribute.yml +5 -5
  11. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/.github/workflows/test.yml +93 -17
  12. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/.github/workflows/zizmor.yml +2 -2
  13. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/.gitignore +2 -1
  14. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/.pre-commit-config.yaml +2 -2
  15. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/PKG-INFO +25 -6
  16. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/README.md +22 -3
  17. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/examples/calver_app.py +11 -5
  18. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/examples/mounted_subapps_app.py +4 -6
  19. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/examples/multi_router_app.py +4 -5
  20. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/examples/openapi_hook_app.py +11 -5
  21. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/examples/self_hosted_docs_app.py +0 -5
  22. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/examples/semver_app.py +36 -5
  23. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/examples/semver_major_only_app.py +11 -5
  24. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/examples/validation_override_integration_app.py +4 -5
  25. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/examples/webhook_versioning_app.py +11 -5
  26. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/fastapi_router_versioning/__init__.py +1 -1
  27. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/fastapi_router_versioning/versioner.py +148 -73
  28. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/pyproject.toml +1 -1
  29. fastapi_router_versioning-1.0.0/RELEASE_NOTES.md → fastapi_router_versioning-1.0.2/release-notes.md +39 -0
  30. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/scripts/prepare_release.py +20 -6
  31. fastapi_router_versioning-1.0.2/tests/test_examples.py +53 -0
  32. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/tests/test_prepare_release.py +124 -3
  33. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/tests/test_versioner.py +376 -5
  34. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/uv.lock +77 -77
  35. fastapi_router_versioning-1.0.0/.github/workflows/labeler.yml +0 -37
  36. fastapi_router_versioning-1.0.0/.github/workflows/latest-changes.yml +0 -50
  37. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/.github/dependabot.yml +0 -0
  38. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/.python-version +0 -0
  39. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/LICENSE +0 -0
  40. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/examples/download_static_assets.py +0 -0
  41. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/fastapi_router_versioning/py.typed +0 -0
  42. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/scripts/format.sh +0 -0
  43. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/scripts/lint.sh +0 -0
  44. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/scripts/test-cov-html.sh +0 -0
  45. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/scripts/test-cov.sh +0 -0
  46. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/scripts/test.sh +0 -0
  47. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/tests/__init__.py +0 -0
  48. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/tests/test_calver.py +0 -0
  49. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/tests/test_fastapi_integration.py +0 -0
  50. {fastapi_router_versioning-1.0.0 → fastapi_router_versioning-1.0.2}/tests/test_semver.py +0 -0
@@ -3,7 +3,7 @@ docs:
3
3
  - changed-files:
4
4
  - any-glob-to-any-file:
5
5
  - README.md
6
- - RELEASE_NOTES.md
6
+ - release-notes.md
7
7
  - all-globs-to-all-files:
8
8
  - '!fastapi_router_versioning/**'
9
9
  - '!pyproject.toml'
@@ -17,16 +17,15 @@ jobs:
17
17
  env:
18
18
  GITHUB_CONTEXT: ${{ toJson(github) }}
19
19
  run: echo "$GITHUB_CONTEXT"
20
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
20
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 #sync-pin:checkout
21
21
  with:
22
- token: ${{ secrets.FASTAPI_ROUTER_VERSIONING_LATEST_CHANGES }}
23
- persist-credentials: true
22
+ persist-credentials: false
24
23
  - name: Set up Python
25
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
24
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 #sync-pin:setup-python
26
25
  with:
27
26
  python-version-file: ".python-version"
28
27
  - name: Setup uv
29
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
28
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 #sync-pin:setup-uv
30
29
  with:
31
30
  # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
32
31
  # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
@@ -52,6 +51,7 @@ jobs:
52
51
  git switch -C "$branch"
53
52
  git add .pre-commit-config.yaml
54
53
  git commit -m "⬆ Bump pre-commit hooks"
54
+ gh auth setup-git
55
55
  git push --force origin "$branch"
56
56
  if [ -z "$(gh pr list --head "$branch" --state open --json number --jq '.[].number')" ]; then
57
57
  gh pr create \
@@ -16,22 +16,22 @@ jobs:
16
16
  contents: write
17
17
  env:
18
18
  PREPARE_RELEASE_VERSION_FILE: fastapi_router_versioning/__init__.py
19
- PREPARE_RELEASE_RELEASE_NOTES_FILE: RELEASE_NOTES.md
19
+ PREPARE_RELEASE_RELEASE_NOTES_FILE: release-notes.md
20
20
  steps:
21
21
  - name: Dump GitHub context
22
22
  env:
23
23
  GITHUB_CONTEXT: ${{ toJson(github) }}
24
24
  run: echo "$GITHUB_CONTEXT"
25
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
25
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 #sync-pin:checkout
26
26
  with:
27
27
  ref: ${{ github.event.repository.default_branch }}
28
- persist-credentials: true
28
+ persist-credentials: false
29
29
  - name: Set up Python
30
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
30
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 #sync-pin:setup-python
31
31
  with:
32
32
  python-version-file: ".python-version"
33
33
  - name: Install uv
34
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
34
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 #sync-pin:setup-uv
35
35
  with:
36
36
  # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
37
37
  # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
@@ -15,7 +15,7 @@ jobs:
15
15
  timeout-minutes: 5
16
16
  steps:
17
17
  - name: Check if PRs have merge conflicts
18
- uses: eps1lon/actions-label-merge-conflict@0273be72a0bbd58fcd71d0d6c02c209b50d1e5e1 # v3.1.0
18
+ uses: eps1lon/actions-label-merge-conflict@0273be72a0bbd58fcd71d0d6c02c209b50d1e5e1 # v3.1.0 #sync-pin:actions-label-merge-conflict
19
19
  with:
20
20
  dirtyLabel: "conflicts"
21
21
  repoToken: "${{ secrets.GITHUB_TOKEN }}"
@@ -18,7 +18,7 @@ jobs:
18
18
  timeout-minutes: 5
19
19
  steps:
20
20
  - name: Check if author is org member or allowed bot
21
- uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
21
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 #sync-pin:github-script
22
22
  with:
23
23
  script: |
24
24
  const pr = context.payload.pull_request;
@@ -0,0 +1,20 @@
1
+ name: Labels
2
+ on:
3
+ pull_request_target: # zizmor: ignore[dangerous-triggers]
4
+ types:
5
+ - opened
6
+ - synchronize
7
+ - reopened
8
+
9
+ permissions: {}
10
+
11
+ jobs:
12
+ labeler:
13
+ permissions:
14
+ contents: read
15
+ pull-requests: write
16
+ runs-on: ubuntu-latest
17
+ timeout-minutes: 5
18
+ steps:
19
+ - uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0 #sync-pin:labeler
20
+ - run: echo "Done adding labels"
@@ -18,19 +18,19 @@ jobs:
18
18
  run: echo "$GITHUB_CONTEXT"
19
19
 
20
20
  - name: Checkout code
21
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
21
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 #sync-pin:checkout
22
22
  with:
23
23
  persist-credentials: false
24
24
  # Full history required for --from-ref/--to-ref diff in prek
25
25
  fetch-depth: 0
26
26
 
27
27
  - name: Set up Python
28
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
28
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 #sync-pin:setup-python
29
29
  with:
30
30
  python-version-file: ".python-version"
31
31
 
32
32
  - name: Setup uv
33
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
33
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 #sync-pin:setup-uv
34
34
  with:
35
35
  # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
36
36
  # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
@@ -60,6 +60,6 @@ jobs:
60
60
  run: echo "$GITHUB_CONTEXT"
61
61
 
62
62
  - name: Decide whether the needed jobs succeeded or failed
63
- uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
63
+ uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2 #sync-pin:alls-green
64
64
  with:
65
65
  jobs: ${{ toJSON(needs) }}
@@ -28,22 +28,21 @@ jobs:
28
28
  pull-requests: write
29
29
  env:
30
30
  PREPARE_RELEASE_VERSION_FILE: fastapi_router_versioning/__init__.py
31
- PREPARE_RELEASE_RELEASE_NOTES_FILE: RELEASE_NOTES.md
31
+ PREPARE_RELEASE_RELEASE_NOTES_FILE: release-notes.md
32
32
  steps:
33
33
  - name: Dump GitHub context
34
34
  env:
35
35
  GITHUB_CONTEXT: ${{ toJson(github) }}
36
36
  run: echo "$GITHUB_CONTEXT"
37
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
37
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 #sync-pin:checkout
38
38
  with:
39
- token: ${{ secrets.FASTAPI_ROUTER_VERSIONING_LATEST_CHANGES }} # zizmor: ignore[secrets-outside-env]
40
- persist-credentials: true
39
+ persist-credentials: false
41
40
  - name: Set up Python
42
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
41
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 #sync-pin:setup-python
43
42
  with:
44
43
  python-version-file: ".python-version"
45
44
  - name: Install uv
46
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
45
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 #sync-pin:setup-uv
47
46
  with:
48
47
  # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
49
48
  # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
@@ -76,6 +75,7 @@ jobs:
76
75
  git switch -c "$branch"
77
76
  git add $PREPARE_RELEASE_VERSION_FILE $PREPARE_RELEASE_RELEASE_NOTES_FILE
78
77
  git commit -m "🔖 Release version ${VERSION}"
78
+ gh auth setup-git
79
79
  git push --set-upstream origin "$branch"
80
80
  gh pr create \
81
81
  --base main \
@@ -20,15 +20,15 @@ jobs:
20
20
  env:
21
21
  GITHUB_CONTEXT: ${{ toJson(github) }}
22
22
  run: echo "$GITHUB_CONTEXT"
23
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
23
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 #sync-pin:checkout
24
24
  with:
25
25
  persist-credentials: false
26
26
  - name: Set up Python
27
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
27
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 #sync-pin:setup-python
28
28
  with:
29
29
  python-version-file: ".python-version"
30
30
  - name: Install uv
31
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
31
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 #sync-pin:setup-uv
32
32
  with:
33
33
  # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
34
34
  # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
@@ -18,10 +18,10 @@ jobs:
18
18
  outputs:
19
19
  src: ${{ steps.filter.outputs.src }}
20
20
  steps:
21
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
21
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 #sync-pin:checkout
22
22
  with:
23
23
  persist-credentials: false
24
- - uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2
24
+ - uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2 #sync-pin:paths-filter
25
25
  id: filter
26
26
  with:
27
27
  filters: |
@@ -46,11 +46,11 @@ jobs:
46
46
  env:
47
47
  GITHUB_CONTEXT: ${{ toJson(github) }}
48
48
  run: echo "$GITHUB_CONTEXT"
49
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
49
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 #sync-pin:checkout-2
50
50
  with:
51
51
  persist-credentials: false
52
52
  - name: Set up Python
53
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
53
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 #sync-pin:setup-python
54
54
  with:
55
55
  python-version-file: ".python-version"
56
56
  - name: Install build dependencies
@@ -83,7 +83,7 @@ jobs:
83
83
  timeout-minutes: 5
84
84
  steps:
85
85
  - name: Decide whether the needed jobs succeeded or failed
86
- uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
86
+ uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2 #sync-pin:alls-green
87
87
  with:
88
88
  jobs: ${{ toJSON(needs) }}
89
89
  allowed-skips: test-redistribute
@@ -19,23 +19,23 @@ jobs:
19
19
  runs-on: ubuntu-latest
20
20
  # Required permissions
21
21
  permissions:
22
- contents: read
23
22
  pull-requests: read
24
23
  timeout-minutes: 5
25
24
  # Set job outputs to values from filter step
26
25
  outputs:
27
26
  src: ${{ steps.filter.outputs.src }}
28
27
  steps:
29
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
28
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 #sync-pin:checkout
30
29
  with:
31
30
  persist-credentials: false
32
31
  # For pull requests it's not necessary to checkout the code but for the main branch it is
33
- - uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2
32
+ - uses: dorny/paths-filter@7b450fff21473bca461d4b92ce414b9d0420d706 # v4.0.2 #sync-pin:paths-filter
34
33
  id: filter
35
34
  with:
36
35
  filters: |
37
36
  src:
38
37
  - .github/workflows/test.yml
38
+ - examples/**
39
39
  - fastapi_router_versioning/**
40
40
  - scripts/**
41
41
  - tests/**
@@ -48,8 +48,6 @@ jobs:
48
48
  - changes
49
49
  if: needs.changes.outputs.src == 'true' || github.ref == 'refs/heads/main'
50
50
  timeout-minutes: 15
51
- permissions:
52
- contents: read
53
51
  strategy:
54
52
  fail-fast: false
55
53
  matrix:
@@ -72,17 +70,17 @@ jobs:
72
70
  GITHUB_CONTEXT: ${{ toJson(github) }}
73
71
  run: echo "$GITHUB_CONTEXT"
74
72
 
75
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
73
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 #sync-pin:checkout-2
76
74
  with:
77
75
  persist-credentials: false
78
76
 
79
77
  - name: Set up Python
80
- uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
78
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 #sync-pin:setup-python
81
79
  with:
82
80
  python-version: ${{ matrix.python-version }}
83
81
 
84
82
  - name: Setup uv
85
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
83
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 #sync-pin:setup-uv
86
84
  with:
87
85
  # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
88
86
  # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
@@ -104,6 +102,7 @@ jobs:
104
102
  run: mkdir coverage
105
103
 
106
104
  - name: Test
105
+ # GitHub Windows runners ship bash natively
107
106
  run: uv run --no-sync bash scripts/test-cov.sh
108
107
  env:
109
108
  COVERAGE_FILE: coverage/.coverage.py${{ matrix.python-version }}-fa${{ matrix.fastapi-version }}
@@ -111,31 +110,107 @@ jobs:
111
110
 
112
111
  - name: Store coverage files
113
112
  if: matrix.coverage == 'coverage'
114
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
113
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 #sync-pin:upload-artifact
115
114
  with:
116
115
  name: coverage-${{ matrix.python-version }}-${{ matrix.fastapi-version }}
117
116
  path: coverage
118
117
  include-hidden-files: true
119
118
 
119
+ regression-test:
120
+ runs-on: ubuntu-latest
121
+ timeout-minutes: 10
122
+ steps:
123
+ - name: Check out the pull request
124
+ if: github.event_name == 'pull_request'
125
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 #sync-pin:checkout-3
126
+ with:
127
+ ref: ${{ github.sha }}
128
+ path: pr
129
+ persist-credentials: false
130
+ fetch-depth: 0
131
+
132
+ - name: Find changed tests
133
+ if: github.event_name == 'pull_request'
134
+ id: changed-tests
135
+ working-directory: pr
136
+ env:
137
+ BASE_SHA: ${{ github.event.pull_request.base.sha }}
138
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
139
+ run: |
140
+ git diff --name-only --diff-filter=AM -z "$BASE_SHA" "$HEAD_SHA" -- tests \
141
+ | while IFS= read -r -d '' file; do
142
+ case "$(basename "$file")" in
143
+ test_*.py) printf '%s\0' "$file" ;;
144
+ esac
145
+ done > "$RUNNER_TEMP/changed-tests"
146
+ if [ -s "$RUNNER_TEMP/changed-tests" ]; then
147
+ echo "found=true" >> "$GITHUB_OUTPUT"
148
+ git diff --binary "$BASE_SHA" "$HEAD_SHA" -- tests \
149
+ > "$RUNNER_TEMP/tests.patch"
150
+ else
151
+ echo "found=false" >> "$GITHUB_OUTPUT"
152
+ echo "No added or modified test files; regression proof is not applicable."
153
+ fi
154
+
155
+ - name: Check out the base revision
156
+ if: steps.changed-tests.outputs.found == 'true'
157
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 #sync-pin:checkout-4
158
+ with:
159
+ ref: ${{ github.event.pull_request.base.sha }}
160
+ path: base
161
+ persist-credentials: false
162
+
163
+ - name: Set up Python
164
+ if: steps.changed-tests.outputs.found == 'true'
165
+ uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 #sync-pin:setup-python-2
166
+ with:
167
+ python-version-file: "base/.python-version"
168
+
169
+ - name: Setup uv
170
+ if: steps.changed-tests.outputs.found == 'true'
171
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 #sync-pin:setup-uv-2
172
+ with:
173
+ # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
174
+ # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
175
+ version: "0.11.18"
176
+ enable-cache: true
177
+
178
+ - name: Run the changed tests against the base code
179
+ if: steps.changed-tests.outputs.found == 'true'
180
+ working-directory: base
181
+ run: |
182
+ git apply "$RUNNER_TEMP/tests.patch"
183
+ uv sync --locked
184
+ set +e
185
+ xargs -0 uv run --no-sync pytest -- < "$RUNNER_TEMP/changed-tests"
186
+ status=$?
187
+ set -e
188
+ if [ "$status" -eq 0 ]; then
189
+ echo "::warning::The changed tests already pass on the base revision. Check whether the fix is still needed."
190
+ echo "### Regression proof: base already passes :warning:" >> "$GITHUB_STEP_SUMMARY"
191
+ echo "The changed tests pass without the pull request's code changes." >> "$GITHUB_STEP_SUMMARY"
192
+ else
193
+ echo "The changed tests fail on the base revision as expected (pytest exit code $status)."
194
+ echo "### Regression proof: base fails as expected :white_check_mark:" >> "$GITHUB_STEP_SUMMARY"
195
+ fi
196
+
120
197
  coverage-combine:
121
198
  needs:
122
199
  - test
123
200
  runs-on: ubuntu-latest
124
201
  timeout-minutes: 5
125
- permissions:
126
- contents: read
127
202
  steps:
128
203
  - name: Dump GitHub context
129
204
  env:
130
205
  GITHUB_CONTEXT: ${{ toJson(github) }}
131
206
  run: echo "$GITHUB_CONTEXT"
132
207
 
133
- - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
208
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 #sync-pin:checkout-5
134
209
  with:
135
210
  persist-credentials: false
136
211
 
137
212
  - name: Setup uv
138
- uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
213
+ uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 #sync-pin:setup-uv-3
139
214
  with:
140
215
  # Before upgrading uv version, make sure astral-sh/setup-uv knows its checksum.
141
216
  # See: https://github.com/astral-sh/setup-uv/issues/851#issuecomment-4282017837
@@ -149,7 +224,7 @@ jobs:
149
224
  run: uv sync --locked
150
225
 
151
226
  - name: Get coverage files
152
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
227
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 #sync-pin:download-artifact
153
228
  with:
154
229
  pattern: coverage-*
155
230
  path: coverage
@@ -161,14 +236,14 @@ jobs:
161
236
  - run: uv run coverage xml
162
237
 
163
238
  - name: Store coverage HTML
164
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
239
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 #sync-pin:upload-artifact-2
165
240
  with:
166
241
  name: coverage-html
167
242
  path: htmlcov
168
243
  include-hidden-files: true
169
244
 
170
245
  - name: Upload coverage to Codecov
171
- uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
246
+ uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 #sync-pin:codecov-action
172
247
  with:
173
248
  token: ${{ secrets.CODECOV_TOKEN }}
174
249
  files: coverage.xml
@@ -182,6 +257,7 @@ jobs:
182
257
  needs:
183
258
  - test
184
259
  - coverage-combine
260
+ - regression-test
185
261
  runs-on: ubuntu-latest
186
262
  timeout-minutes: 5
187
263
  steps:
@@ -190,7 +266,7 @@ jobs:
190
266
  GITHUB_CONTEXT: ${{ toJson(github) }}
191
267
  run: echo "$GITHUB_CONTEXT"
192
268
  - name: Decide whether the needed jobs succeeded or failed
193
- uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2
269
+ uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe # v1.2.2 #sync-pin:alls-green
194
270
  with:
195
271
  jobs: ${{ toJSON(needs) }}
196
272
  allowed-skips: test,coverage-combine
@@ -18,8 +18,8 @@ jobs:
18
18
  security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files.
19
19
  steps:
20
20
  - name: Checkout repository
21
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
21
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 #sync-pin:checkout
22
22
  with:
23
23
  persist-credentials: false
24
24
  - name: Run zizmor
25
- uses: zizmorcore/zizmor-action@6599ee8b7a49aef6a770f63d261d214911a7ce02 # v0.6.0
25
+ uses: zizmorcore/zizmor-action@3dc1ecc9bcb9e94e9b2c709687979e1298497054 # v0.6.2 #sync-pin:zizmor-action
@@ -31,5 +31,6 @@ Thumbs.db
31
31
  *.tmp
32
32
 
33
33
  # Local environment
34
- .env
35
34
  .run/
35
+ /CLAUDE.md
36
+ /.claude/
@@ -38,14 +38,14 @@ repos:
38
38
 
39
39
  - id: local-mypy
40
40
  name: mypy check
41
- entry: uv run mypy fastapi_router_versioning
41
+ entry: uv run mypy fastapi_router_versioning examples
42
42
  require_serial: true
43
43
  language: unsupported
44
44
  pass_filenames: false
45
45
 
46
46
  - id: local-ty
47
47
  name: ty check
48
- entry: uv run ty check fastapi_router_versioning
48
+ entry: uv run ty check fastapi_router_versioning examples
49
49
  require_serial: true
50
50
  language: unsupported
51
51
  pass_filenames: false
@@ -1,12 +1,12 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: fastapi-router-versioning
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: Router-based API versioning for FastAPI, with per-version docs and a declarative route lifecycle.
5
5
  Project-URL: Homepage, https://github.com/mat81black/fastapi-router-versioning
6
6
  Project-URL: Repository, https://github.com/mat81black/fastapi-router-versioning
7
7
  Project-URL: Issues, https://github.com/mat81black/fastapi-router-versioning/issues
8
8
  Project-URL: Documentation, https://github.com/mat81black/fastapi-router-versioning#readme
9
- Project-URL: Changelog, https://github.com/mat81black/fastapi-router-versioning/blob/main/RELEASE_NOTES.md
9
+ Project-URL: Changelog, https://github.com/mat81black/fastapi-router-versioning/blob/main/release-notes.md
10
10
  Author-email: Matteo Nieddu <mat81black@gmail.com>
11
11
  License-Expression: MIT
12
12
  License-File: LICENSE
@@ -150,6 +150,9 @@ def legacy_route():
150
150
  | v2.0 | yes | **yes** |
151
151
  | v3.0 | no | n/a |
152
152
 
153
+ `remove_in` is optional: `deprecate_in` alone marks a route deprecated from that version
154
+ onward with no planned removal, staying available (and deprecated) in every later version.
155
+
153
156
  A route without `@api_version` isn't excluded, it falls back to `default_version`
154
157
  (`(1, 0)` for SemVer, `"1"` for CalVer, unless overridden).
155
158
 
@@ -162,6 +165,11 @@ shows as deprecated in every version, including ones before `deprecate_in`'s bou
162
165
  Use one or the other: `deprecated=True` for an unconditional, version-independent flag,
163
166
  `deprecate_in` for a per-version lifecycle.
164
167
 
168
+ A route with `methods=["GET", "POST"]` can have just one of its methods taken over by a
169
+ dedicated route in a later version; the other method keeps being served by the original
170
+ route. See [`semver_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/semver_app.py)
171
+ for a working example.
172
+
165
173
  ---
166
174
 
167
175
  ## `RouterVersioner` reference
@@ -252,6 +260,10 @@ GET /versions
252
260
  }
253
261
  ```
254
262
 
263
+ If several `RouterVersioner` instances share one app and all set `include_versions_route=True`,
264
+ `/versions` is mounted once and lists every instance's versions together, instead of the
265
+ first instance shadowing the rest (see [Multiple routers](#multiple-routers)).
266
+
255
267
  ### Custom URL format
256
268
 
257
269
  `prefix_format` and `semantic_version_format` control how a version renders in URLs and in
@@ -422,7 +434,9 @@ pass them all to one `RouterVersioner` via `routers=[...]` instead (see
422
434
  If you do share an app across instances, one rule is enforced for you: every instance needs
423
435
  its own `prefix_format`/`latest_prefix`. Two instances that resolve to the same prefix would
424
436
  otherwise overwrite each other's docs/openapi routes at the same path; this raises
425
- `RuntimeError`.
437
+ `RuntimeError`. `/versions` (see [Version discovery endpoint](#version-discovery-endpoint))
438
+ doesn't need this coordination: every instance's contribution is aggregated into the same
439
+ endpoint automatically.
426
440
 
427
441
  For modules that genuinely don't need to coordinate at all, mount them as separate FastAPI
428
442
  sub-applications instead (next section).
@@ -460,13 +474,18 @@ RouterVersioner(
460
474
  ).versionize()
461
475
  ```
462
476
 
477
+ If `versionize()` raises (e.g. the callback itself throws), don't catch the exception and
478
+ retry: some versions may already be mounted, and this package makes no attempt to undo that.
479
+ `versionize()` normally runs at startup, so the app simply fails to start — fix the underlying
480
+ issue and start it again.
481
+
463
482
  ---
464
483
 
465
484
  ## Examples
466
485
 
467
486
  | File | What it shows |
468
487
  |---|---|
469
- | [`semver_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/semver_app.py) | Full SemVer lifecycle: introduce, deprecate, remove |
488
+ | [`semver_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/semver_app.py) | Full SemVer lifecycle: introduce, deprecate, remove, permanent deprecation, multi-method route with partial takeover |
470
489
  | [`calver_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/calver_app.py) | Same lifecycle, CalVer date strings instead |
471
490
  | [`semver_major_only_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/semver_major_only_app.py) | Major-only URLs (`/v1`, `/v2`) via `prefix_format` |
472
491
  | [`webhook_versioning_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/webhook_versioning_app.py) | Per-version webhook definitions via `webhook_routers` |
@@ -480,7 +499,7 @@ RouterVersioner(
480
499
 
481
500
  ## Release Notes
482
501
 
483
- [RELEASE_NOTES](https://github.com/mat81black/fastapi-router-versioning/blob/main/RELEASE_NOTES.md)
502
+ [RELEASE_NOTES](https://github.com/mat81black/fastapi-router-versioning/blob/main/release-notes.md)
484
503
 
485
504
  ---
486
505
 
@@ -119,6 +119,9 @@ def legacy_route():
119
119
  | v2.0 | yes | **yes** |
120
120
  | v3.0 | no | n/a |
121
121
 
122
+ `remove_in` is optional: `deprecate_in` alone marks a route deprecated from that version
123
+ onward with no planned removal, staying available (and deprecated) in every later version.
124
+
122
125
  A route without `@api_version` isn't excluded, it falls back to `default_version`
123
126
  (`(1, 0)` for SemVer, `"1"` for CalVer, unless overridden).
124
127
 
@@ -131,6 +134,11 @@ shows as deprecated in every version, including ones before `deprecate_in`'s bou
131
134
  Use one or the other: `deprecated=True` for an unconditional, version-independent flag,
132
135
  `deprecate_in` for a per-version lifecycle.
133
136
 
137
+ A route with `methods=["GET", "POST"]` can have just one of its methods taken over by a
138
+ dedicated route in a later version; the other method keeps being served by the original
139
+ route. See [`semver_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/semver_app.py)
140
+ for a working example.
141
+
134
142
  ---
135
143
 
136
144
  ## `RouterVersioner` reference
@@ -221,6 +229,10 @@ GET /versions
221
229
  }
222
230
  ```
223
231
 
232
+ If several `RouterVersioner` instances share one app and all set `include_versions_route=True`,
233
+ `/versions` is mounted once and lists every instance's versions together, instead of the
234
+ first instance shadowing the rest (see [Multiple routers](#multiple-routers)).
235
+
224
236
  ### Custom URL format
225
237
 
226
238
  `prefix_format` and `semantic_version_format` control how a version renders in URLs and in
@@ -391,7 +403,9 @@ pass them all to one `RouterVersioner` via `routers=[...]` instead (see
391
403
  If you do share an app across instances, one rule is enforced for you: every instance needs
392
404
  its own `prefix_format`/`latest_prefix`. Two instances that resolve to the same prefix would
393
405
  otherwise overwrite each other's docs/openapi routes at the same path; this raises
394
- `RuntimeError`.
406
+ `RuntimeError`. `/versions` (see [Version discovery endpoint](#version-discovery-endpoint))
407
+ doesn't need this coordination: every instance's contribution is aggregated into the same
408
+ endpoint automatically.
395
409
 
396
410
  For modules that genuinely don't need to coordinate at all, mount them as separate FastAPI
397
411
  sub-applications instead (next section).
@@ -429,13 +443,18 @@ RouterVersioner(
429
443
  ).versionize()
430
444
  ```
431
445
 
446
+ If `versionize()` raises (e.g. the callback itself throws), don't catch the exception and
447
+ retry: some versions may already be mounted, and this package makes no attempt to undo that.
448
+ `versionize()` normally runs at startup, so the app simply fails to start — fix the underlying
449
+ issue and start it again.
450
+
432
451
  ---
433
452
 
434
453
  ## Examples
435
454
 
436
455
  | File | What it shows |
437
456
  |---|---|
438
- | [`semver_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/semver_app.py) | Full SemVer lifecycle: introduce, deprecate, remove |
457
+ | [`semver_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/semver_app.py) | Full SemVer lifecycle: introduce, deprecate, remove, permanent deprecation, multi-method route with partial takeover |
439
458
  | [`calver_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/calver_app.py) | Same lifecycle, CalVer date strings instead |
440
459
  | [`semver_major_only_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/semver_major_only_app.py) | Major-only URLs (`/v1`, `/v2`) via `prefix_format` |
441
460
  | [`webhook_versioning_app.py`](https://github.com/mat81black/fastapi-router-versioning/blob/main/examples/webhook_versioning_app.py) | Per-version webhook definitions via `webhook_routers` |
@@ -449,7 +468,7 @@ RouterVersioner(
449
468
 
450
469
  ## Release Notes
451
470
 
452
- [RELEASE_NOTES](https://github.com/mat81black/fastapi-router-versioning/blob/main/RELEASE_NOTES.md)
471
+ [RELEASE_NOTES](https://github.com/mat81black/fastapi-router-versioning/blob/main/release-notes.md)
453
472
 
454
473
  ---
455
474