traffik 1.0.0b1__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 (66) hide show
  1. traffik-1.0.0b1/.coverage +0 -0
  2. traffik-1.0.0b1/.dockerignore +111 -0
  3. traffik-1.0.0b1/.github/CODEOWNERS +1 -0
  4. traffik-1.0.0b1/.github/workflows/code-quality.yaml +83 -0
  5. traffik-1.0.0b1/.github/workflows/enforce-pr-source.yaml +47 -0
  6. traffik-1.0.0b1/.github/workflows/publish.yaml +345 -0
  7. traffik-1.0.0b1/.github/workflows/test.yaml +207 -0
  8. traffik-1.0.0b1/.gitignore +9 -0
  9. traffik-1.0.0b1/CHANGELOG.md +0 -0
  10. traffik-1.0.0b1/CONTRIBUTING.md +461 -0
  11. traffik-1.0.0b1/DOCKER.md +258 -0
  12. traffik-1.0.0b1/Dockerfile +43 -0
  13. traffik-1.0.0b1/LICENSE +21 -0
  14. traffik-1.0.0b1/Makefile +175 -0
  15. traffik-1.0.0b1/PKG-INFO +1720 -0
  16. traffik-1.0.0b1/README.md +1631 -0
  17. traffik-1.0.0b1/TESTING.md +313 -0
  18. traffik-1.0.0b1/docker-compose.yml +221 -0
  19. traffik-1.0.0b1/docker-test.sh +259 -0
  20. traffik-1.0.0b1/pyproject.toml +110 -0
  21. traffik-1.0.0b1/src/traffik/__init__.py +21 -0
  22. traffik-1.0.0b1/src/traffik/backends/__init__.py +0 -0
  23. traffik-1.0.0b1/src/traffik/backends/base.py +536 -0
  24. traffik-1.0.0b1/src/traffik/backends/inmemory.py +398 -0
  25. traffik-1.0.0b1/src/traffik/backends/memcached.py +555 -0
  26. traffik-1.0.0b1/src/traffik/backends/redis.py +411 -0
  27. traffik-1.0.0b1/src/traffik/decorators.py +211 -0
  28. traffik-1.0.0b1/src/traffik/exceptions.py +131 -0
  29. traffik-1.0.0b1/src/traffik/middleware.py +240 -0
  30. traffik-1.0.0b1/src/traffik/py.typed +0 -0
  31. traffik-1.0.0b1/src/traffik/rates.py +195 -0
  32. traffik-1.0.0b1/src/traffik/strategies/__init__.py +7 -0
  33. traffik-1.0.0b1/src/traffik/strategies/fixed_window.py +133 -0
  34. traffik-1.0.0b1/src/traffik/strategies/leaky_bucket.py +256 -0
  35. traffik-1.0.0b1/src/traffik/strategies/sliding_window.py +257 -0
  36. traffik-1.0.0b1/src/traffik/strategies/token_bucket.py +333 -0
  37. traffik-1.0.0b1/src/traffik/throttles.py +322 -0
  38. traffik-1.0.0b1/src/traffik/types.py +137 -0
  39. traffik-1.0.0b1/src/traffik/utils.py +485 -0
  40. traffik-1.0.0b1/tests/__init__.py +0 -0
  41. traffik-1.0.0b1/tests/asyncio_client.py +354 -0
  42. traffik-1.0.0b1/tests/backends/__init__.py +1 -0
  43. traffik-1.0.0b1/tests/backends/test_base.py +72 -0
  44. traffik-1.0.0b1/tests/backends/test_concurrency.py +135 -0
  45. traffik-1.0.0b1/tests/backends/test_generics.py +790 -0
  46. traffik-1.0.0b1/tests/conftest.py +118 -0
  47. traffik-1.0.0b1/tests/fastapi/__init__.py +1 -0
  48. traffik-1.0.0b1/tests/fastapi/test_decorators.py +118 -0
  49. traffik-1.0.0b1/tests/fastapi/test_middleware.py +867 -0
  50. traffik-1.0.0b1/tests/fastapi/test_throttles.py +329 -0
  51. traffik-1.0.0b1/tests/fastapi/test_throttles_dynamic.py +265 -0
  52. traffik-1.0.0b1/tests/fastapi/test_throttles_multi_service.py +479 -0
  53. traffik-1.0.0b1/tests/starlette/__init__.py +0 -0
  54. traffik-1.0.0b1/tests/starlette/test_middleware.py +893 -0
  55. traffik-1.0.0b1/tests/starlette/test_throttles.py +312 -0
  56. traffik-1.0.0b1/tests/starlette/test_throttles_dynamic.py +277 -0
  57. traffik-1.0.0b1/tests/starlette/test_throttles_multi_service.py +504 -0
  58. traffik-1.0.0b1/tests/strategies/__init__.py +1 -0
  59. traffik-1.0.0b1/tests/strategies/test_edge_cases.py +411 -0
  60. traffik-1.0.0b1/tests/strategies/test_fixed_window.py +195 -0
  61. traffik-1.0.0b1/tests/strategies/test_leaky_bucket.py +345 -0
  62. traffik-1.0.0b1/tests/strategies/test_sliding_window.py +298 -0
  63. traffik-1.0.0b1/tests/strategies/test_token_bucket.py +362 -0
  64. traffik-1.0.0b1/tests/test_rates.py +303 -0
  65. traffik-1.0.0b1/tests/utils.py +11 -0
  66. traffik-1.0.0b1/uv.lock +1172 -0
Binary file
@@ -0,0 +1,111 @@
1
+ # .dockerignore for traffik library
2
+
3
+ # Git
4
+ .git
5
+ .gitignore
6
+
7
+ # Python cache
8
+ __pycache__/
9
+ *.py[cod]
10
+ *$py.class
11
+ *.so
12
+ .Python
13
+ build/
14
+ develop-eggs/
15
+ dist/
16
+ downloads/
17
+ eggs/
18
+ .eggs/
19
+ lib/
20
+ lib64/
21
+ parts/
22
+ sdist/
23
+ var/
24
+ wheels/
25
+ *.egg-info/
26
+ .installed.cfg
27
+ *.egg
28
+
29
+ # Testing
30
+ .pytest_cache/
31
+ .coverage
32
+ htmlcov/
33
+ .tox/
34
+ .cache
35
+ .mypy_cache/
36
+ .dmypy.json
37
+ dmypy.json
38
+
39
+ # IDEs
40
+ .vscode/
41
+ .idea/
42
+ *.swp
43
+ *.swo
44
+ *~
45
+
46
+ # OS
47
+ .DS_Store
48
+ .DS_Store?
49
+ ._*
50
+ .Spotlight-V100
51
+ .Trashes
52
+ ehthumbs.db
53
+ Thumbs.db
54
+
55
+ # Environments
56
+ .env
57
+ .venv
58
+ env/
59
+ venv/
60
+ ENV/
61
+ env.bak/
62
+ venv.bak/
63
+
64
+ # Jupyter Notebook
65
+ .ipynb_checkpoints
66
+
67
+ # Documentation
68
+ docs/_build/
69
+
70
+ # CI/CD
71
+ .github/
72
+
73
+ # Docker
74
+ Dockerfile*
75
+ docker-compose*.yml
76
+ .dockerignore
77
+
78
+ # Package manager
79
+ node_modules/
80
+ npm-debug.log*
81
+
82
+ # Security
83
+ *.pem
84
+ *.key
85
+
86
+ # Logs
87
+ *.log
88
+
89
+ # Temporary files
90
+ tmp/
91
+ temp/
92
+
93
+ # Editor backups
94
+ *.bak
95
+ *.tmp
96
+
97
+ # Coverage reports
98
+ htmlcov/
99
+ .coverage.*
100
+ coverage.xml
101
+ *.cover
102
+ .hypothesis/
103
+
104
+ # Ruff cache
105
+ .ruff_cache/
106
+
107
+ # MyPy cache
108
+ .mypy_cache/
109
+
110
+ # UV cache
111
+ .uv_cache/
@@ -0,0 +1 @@
1
+ * @tioluwa
@@ -0,0 +1,83 @@
1
+ name: Code Quality
2
+
3
+ on:
4
+ # push:
5
+ # branches: [main, develop]
6
+ pull_request:
7
+ branches: [main, develop]
8
+ workflow_dispatch: # Allow manual triggering
9
+ workflow_call: # Allow referencing
10
+
11
+ concurrency:
12
+ group: code-quality-${{ github.workflow }}-${{ github.ref }}
13
+ cancel-in-progress: true
14
+
15
+ jobs:
16
+ quality:
17
+ name: Code Quality Checks
18
+ runs-on: ubuntu-latest
19
+
20
+ steps:
21
+ - name: Checkout code
22
+ uses: actions/checkout@v4
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@v4
26
+ with:
27
+ version: "latest"
28
+
29
+ - name: Set up Python
30
+ uses: actions/setup-python@v5
31
+ with:
32
+ python-version: "3.9"
33
+
34
+ - name: Install dependencies
35
+ run: uv sync --extra dev
36
+
37
+ - name: Check code formatting
38
+ run: |
39
+ echo "Checking code formatting..."
40
+ uv run ruff format src/ tests/
41
+
42
+ - name: Run security analysis
43
+ run: |
44
+ echo "Running Bandit security analysis..."
45
+ uv run bandit -r src/ -f json -o bandit-report.json || true
46
+ uv run bandit -r src/
47
+ continue-on-error: false
48
+
49
+ - name: Upload security reports
50
+ if: always()
51
+ uses: actions/upload-artifact@v4
52
+ with:
53
+ name: security-reports
54
+ path: |
55
+ bandit-report.json
56
+ retention-days: 30
57
+
58
+ type-check:
59
+ name: Type Checking
60
+ runs-on: ubuntu-latest
61
+
62
+ steps:
63
+ - name: Checkout code
64
+ uses: actions/checkout@v4
65
+
66
+ - name: Install uv
67
+ uses: astral-sh/setup-uv@v4
68
+ with:
69
+ version: "latest"
70
+
71
+ - name: Set up Python
72
+ uses: actions/setup-python@v5
73
+ with:
74
+ python-version: "3.9"
75
+
76
+ - name: Install dependencies
77
+ run: uv sync --extra dev
78
+
79
+ - name: Run type checking
80
+ run: |
81
+ echo "Running mypy type checking..."
82
+ uv run python -m mypy src/ --ignore-missing-imports --no-strict-optional
83
+ continue-on-error: true # Optional since you might not have mypy setup yet
@@ -0,0 +1,47 @@
1
+ name: Enforce PR Source Branch and Origin
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+
7
+ jobs:
8
+ enforce-branch-policy:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - name: Checkout code
12
+ uses: actions/checkout@v4
13
+ with:
14
+ fetch-depth: 0 # Required to access full history
15
+
16
+ - name: Add summary
17
+ run: |
18
+ echo "## 🔒 Branch Policy Enforcement" >> $GITHUB_STEP_SUMMARY
19
+ echo "- ✅ Only \`release/*\` and \`hotfix/*\` branches can PR into \`main\`" >> $GITHUB_STEP_SUMMARY
20
+ echo "- ✅ \`release/*\` must originate from \`develop\`" >> $GITHUB_STEP_SUMMARY
21
+ echo "- ✅ \`hotfix/*\` must originate from \`main\`" >> $GITHUB_STEP_SUMMARY
22
+
23
+ - name: Validate PR source branch and origin
24
+ run: |
25
+ SOURCE_BRANCH="${{ github.head_ref }}"
26
+ TARGET_BRANCH="${{ github.base_ref }}"
27
+
28
+ echo "🔍 Checking source: $SOURCE_BRANCH -> target: $TARGET_BRANCH"
29
+
30
+ if [[ "$SOURCE_BRANCH" == release/* ]]; then
31
+ BASE_BRANCH="develop"
32
+ elif [[ "$SOURCE_BRANCH" == hotfix/* ]]; then
33
+ BASE_BRANCH="main"
34
+ else
35
+ echo "❌ Invalid source branch name: must start with release/ or hotfix/"
36
+ exit 1
37
+ fi
38
+
39
+ git fetch origin $BASE_BRANCH
40
+ MERGE_BASE=$(git merge-base origin/$SOURCE_BRANCH origin/$BASE_BRANCH)
41
+
42
+ if [[ "$(git rev-parse origin/$BASE_BRANCH)" != "$MERGE_BASE" ]]; then
43
+ echo "❌ $SOURCE_BRANCH must be based on $BASE_BRANCH"
44
+ exit 1
45
+ fi
46
+
47
+ echo "✅ $SOURCE_BRANCH is valid and based on $BASE_BRANCH"
@@ -0,0 +1,345 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*' # Trigger on version tags like v0.1.0, v1.0.0, etc.
7
+ release:
8
+ types: [published]
9
+ workflow_dispatch: # Allow manual triggering
10
+ inputs:
11
+ test_only:
12
+ description: 'Publish to TestPyPI only'
13
+ type: boolean
14
+ default: true
15
+
16
+ jobs:
17
+ # Run code quality workflow
18
+ quality-gate:
19
+ name: Code Quality Assurance Gate
20
+ uses: ./.github/workflows/code-quality.yaml
21
+
22
+
23
+ # Run the full test suite first
24
+ test-gate:
25
+ name: Test Suite Gate
26
+ uses: ./.github/workflows/test.yaml
27
+
28
+
29
+ # Build and validate package
30
+ build:
31
+ name: Build Package
32
+ runs-on: ubuntu-latest
33
+ needs: [quality-gate, test-gate]
34
+
35
+ steps:
36
+ - name: Checkout code
37
+ uses: actions/checkout@v4
38
+
39
+ - name: Install uv
40
+ uses: astral-sh/setup-uv@v4
41
+ with:
42
+ version: "latest"
43
+
44
+ - name: Set up Python
45
+ uses: actions/setup-python@v5
46
+ with:
47
+ python-version: "3.11"
48
+
49
+ - name: Verify version consistency
50
+ run: |
51
+ # Extract version from pyproject.toml and git tag
52
+ PROJECT_VERSION=$(uv run python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
53
+ if [[ "$GITHUB_REF_TYPE" == "tag" ]]; then
54
+ TAG_VERSION=${GITHUB_REF_NAME#v}
55
+ if [[ "$PROJECT_VERSION" != "$TAG_VERSION" ]]; then
56
+ echo "Version mismatch: pyproject.toml has $PROJECT_VERSION but tag is $TAG_VERSION"
57
+ exit 1
58
+ fi
59
+ fi
60
+ echo "Version: $PROJECT_VERSION"
61
+
62
+ - name: Build package
63
+ run: uv build
64
+
65
+ - name: Validate build artifacts
66
+ run: |
67
+ ls -la dist/
68
+ # Verify both wheel and sdist are created
69
+ test -f dist/*.whl || (echo "No wheel found" && exit 1)
70
+ test -f dist/*.tar.gz || (echo "No source distribution found" && exit 1)
71
+
72
+ # Check package contents
73
+ uv run python -m zipfile -l dist/*.whl
74
+ uv run python -m tarfile -l dist/*.tar.gz
75
+
76
+ echo "✅ Build artifacts created successfully"
77
+
78
+ - name: Install and test built package
79
+ run: |
80
+ # Install the wheel in a clean environment
81
+ uv venv test-env
82
+ uv pip install dist/*.whl
83
+ uv run python -c "
84
+ import traffik
85
+ from traffik.backends.inmemory import InMemoryBackend
86
+ from traffik.throttles import HTTPThrottle
87
+ print('✅ Built package works correctly')
88
+ "
89
+
90
+ - name: Upload build artifacts
91
+ uses: actions/upload-artifact@v4
92
+ with:
93
+ name: dist-${{ github.sha }}
94
+ path: dist/
95
+ retention-days: 30
96
+
97
+
98
+ # Publish to TestPyPI for testing
99
+ publish-testpypi:
100
+ name: Publish to TestPyPI
101
+ runs-on: ubuntu-latest
102
+ needs: build
103
+ if: >
104
+ github.event_name == 'workflow_dispatch' ||
105
+ (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) ||
106
+ github.event_name == 'release'
107
+
108
+ environment:
109
+ name: testpypi
110
+ url: https://test.pypi.org/p/traffik
111
+
112
+ outputs:
113
+ published: ${{ steps.publish-step.outputs.published || steps.skip-step.outputs.published || 'false' }}
114
+ version: ${{ steps.get-version.outputs.version }}
115
+
116
+ steps:
117
+ - name: Checkout code
118
+ uses: actions/checkout@v4
119
+
120
+ - name: Set up Python
121
+ uses: actions/setup-python@v5
122
+ with:
123
+ python-version: "3.11"
124
+
125
+ - name: Install uv
126
+ uses: astral-sh/setup-uv@v4
127
+ with:
128
+ version: "latest"
129
+
130
+ - name: Install jq for version parsing
131
+ run: uv pip install jq --system
132
+
133
+ - name: Get project version
134
+ id: get-version
135
+ run: |
136
+ VERSION=$(uv run python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
137
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
138
+
139
+ - name: Check if version already exists on TestPyPI
140
+ id: version-check
141
+ run: |
142
+ VERSION="${{ steps.get-version.outputs.version }}"
143
+ if curl -sSf https://test.pypi.org/pypi/traffik/json | jq -e ".releases[\"$VERSION\"]" > /dev/null; then
144
+ echo "exists=true" >> "$GITHUB_OUTPUT"
145
+ else
146
+ echo "exists=false" >> "$GITHUB_OUTPUT"
147
+ fi
148
+
149
+ - name: Skip publishing (version exists)
150
+ if: steps.version-check.outputs.exists == 'true'
151
+ run: echo "⚠️ Version already exists on TestPyPI. Skipping publish."
152
+
153
+ - name: Download build artifacts
154
+ uses: actions/download-artifact@v4
155
+ with:
156
+ name: dist-${{ github.sha }}
157
+ path: dist/
158
+
159
+ - name: Publish to TestPyPI
160
+ id: publish-step
161
+ if: steps.version-check.outputs.exists == 'false'
162
+ run: |
163
+ uv publish --index testpypi
164
+ echo "published=true" >> "$GITHUB_OUTPUT"
165
+ env:
166
+ UV_PUBLISH_TOKEN: ${{ secrets.TEST_PYPI_API_TOKEN }}
167
+
168
+ - name: Mark as skipped
169
+ if: steps.version-check.outputs.exists == 'true'
170
+ id: skip-step
171
+ run: echo "published=false" >> "$GITHUB_OUTPUT"
172
+
173
+
174
+ # Verify TestPyPI installation
175
+ verify-testpypi:
176
+ name: Verify TestPyPI Installation
177
+ runs-on: ubuntu-latest
178
+ needs: publish-testpypi
179
+ if: needs.publish-testpypi.result == 'success' && needs.publish-testpypi.outputs.published == 'true'
180
+ continue-on-error: true
181
+
182
+ strategy:
183
+ matrix:
184
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
185
+
186
+ steps:
187
+ - name: Set up Python ${{ matrix.python-version }}
188
+ uses: actions/setup-python@v5
189
+ with:
190
+ python-version: ${{ matrix.python-version }}
191
+
192
+ - name: Wait for TestPyPI availability
193
+ run: |
194
+ echo "⏳ Waiting for package to be available on TestPyPI..."
195
+ sleep 60 # Give TestPyPI time to process
196
+
197
+ - name: Install from TestPyPI
198
+ run: |
199
+ pip install --index-url https://test.pypi.org/simple/ \
200
+ --extra-index-url https://pypi.org/simple/ \
201
+ traffik[all] --prefer-binary
202
+
203
+ - name: Test installation and functionality
204
+ run: |
205
+ python -c "
206
+ import asyncio
207
+ import traffik
208
+ from traffik.backends.inmemory import InMemoryBackend
209
+ from traffik.throttles import HTTPThrottle
210
+
211
+ print('✅ Package imported successfully')
212
+ print(f'Version: {getattr(traffik, \"__version__\", \"unknown\")}')
213
+
214
+ async def test():
215
+ backend = InMemoryBackend()
216
+ async with backend():
217
+ throttle = HTTPThrottle(limit=5, seconds=60)
218
+ print('✅ Basic functionality works')
219
+
220
+ asyncio.run(test())
221
+ "
222
+
223
+
224
+ # Publish to PyPI (production)
225
+ publish-pypi:
226
+ name: Publish to PyPI
227
+ runs-on: ubuntu-latest
228
+ needs: [build, publish-testpypi]
229
+ if: >
230
+ github.event_name == 'release' &&
231
+ github.event.action == 'published' &&
232
+ !github.event.release.prerelease &&
233
+ (github.event.inputs.test_only != 'true' || github.event.inputs.test_only == '') &&
234
+ always() &&
235
+ needs.build.result == 'success' &&
236
+ needs.publish-testpypi.result == 'success'
237
+
238
+ environment:
239
+ name: pypi
240
+ url: https://pypi.org/p/traffik
241
+
242
+ steps:
243
+ - name: Checkout code
244
+ uses: actions/checkout@v4
245
+
246
+ - name: Set up Python
247
+ uses: actions/setup-python@v5
248
+ with:
249
+ python-version: "3.11"
250
+
251
+ - name: Install uv
252
+ uses: astral-sh/setup-uv@v4
253
+ with:
254
+ version: "latest"
255
+
256
+ - name: Install jq for version parsing
257
+ run: uv pip install jq --system
258
+
259
+ - name: Get project version
260
+ id: get-version
261
+ run: |
262
+ VERSION=$(uv run python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])")
263
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
264
+
265
+ - name: Check if version already exists on PyPI
266
+ id: version-check
267
+ run: |
268
+ VERSION="${{ steps.get-version.outputs.version }}"
269
+ if curl -sSf https://pypi.org/pypi/traffik/json | jq -e ".releases[\"$VERSION\"]" > /dev/null; then
270
+ echo "exists=true" >> "$GITHUB_OUTPUT"
271
+ else
272
+ echo "exists=false" >> "$GITHUB_OUTPUT"
273
+ fi
274
+
275
+ - name: Skip publishing (version exists)
276
+ if: steps.version-check.outputs.exists == 'true'
277
+ run: echo "⚠️ Version already exists on PyPI. Skipping publish."
278
+
279
+ - name: Download build artifacts
280
+ uses: actions/download-artifact@v4
281
+ with:
282
+ name: dist-${{ github.sha }}
283
+ path: dist/
284
+
285
+ - name: Final validation before PyPI
286
+ if: steps.version-check.outputs.exists == 'false'
287
+ run: |
288
+ echo "🚀 Publishing to PyPI..."
289
+ ls -la dist/
290
+
291
+ # Double-check we're not publishing a dev version
292
+ if uv run python -c "import tomllib; version=tomllib.load(open('pyproject.toml', 'rb'))['project']['version']; exit(1 if 'dev' in version or 'alpha' in version or 'beta' in version or 'rc' in version else 0)"; then
293
+ echo "✅ Version looks stable for PyPI"
294
+ else
295
+ echo "❌ Version appears to be a pre-release"
296
+ exit 1
297
+ fi
298
+
299
+ - name: Publish to PyPI
300
+ if: steps.version-check.outputs.exists == 'false'
301
+ run: uv publish
302
+ env:
303
+ UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
304
+
305
+
306
+ # Post-publish verification
307
+ verify-pypi:
308
+ name: Verify PyPI Installation
309
+ runs-on: ubuntu-latest
310
+ needs: publish-pypi
311
+ if: success()
312
+
313
+ steps:
314
+ - name: Set up Python
315
+ uses: actions/setup-python@v5
316
+ with:
317
+ python-version: "3.11"
318
+
319
+ - name: Wait for PyPI availability
320
+ run: |
321
+ echo "⏳ Waiting for package to be available on PyPI..."
322
+ sleep 120 # Give PyPI time to process
323
+
324
+ - name: Install from PyPI
325
+ run: pip install traffik[all]
326
+
327
+ - name: Verify installation
328
+ run: |
329
+ python -c "
330
+ import traffik
331
+ print(f'✅🎉 Successfully installed traffik {getattr(traffik, \"__version__\", \"unknown\")} from PyPI')
332
+ "
333
+
334
+ - name: Create release summary
335
+ run: |
336
+ echo "## 🎉 Release Summary" >> $GITHUB_STEP_SUMMARY
337
+ echo "- ✅ Quality checks passed" >> $GITHUB_STEP_SUMMARY
338
+ echo "- ✅ All tests passed" >> $GITHUB_STEP_SUMMARY
339
+ echo "- ✅ Security checks passed" >> $GITHUB_STEP_SUMMARY
340
+ echo "- ✅ Package built successfully" >> $GITHUB_STEP_SUMMARY
341
+ echo "- ✅ Published to TestPyPI" >> $GITHUB_STEP_SUMMARY
342
+ echo "- ✅ TestPyPI installation verified" >> $GITHUB_STEP_SUMMARY
343
+ echo "- ✅ Published to PyPI" >> $GITHUB_STEP_SUMMARY
344
+ echo "" >> $GITHUB_STEP_SUMMARY
345
+ echo "📦 Package is now available: \`pip install traffik\`" >> $GITHUB_STEP_SUMMARY