DefStream 0.1.3__tar.gz → 0.1.3.5__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 (85) hide show
  1. defstream-0.1.3.5/.github/workflows/ci.yml +403 -0
  2. defstream-0.1.3.5/.gitignore +62 -0
  3. defstream-0.1.3.5/DefStream.egg-info/PKG-INFO +538 -0
  4. {defstream-0.1.3 → defstream-0.1.3.5}/DefStream.egg-info/SOURCES.txt +10 -2
  5. defstream-0.1.3.5/PKG-INFO +538 -0
  6. defstream-0.1.3.5/README.en.md +518 -0
  7. defstream-0.1.3.5/README.md +505 -0
  8. defstream-0.1.3.5/sample/Broker/Sample.py +227 -0
  9. defstream-0.1.3.5/sample/Worker/RabbitMQ/Client.py +77 -0
  10. defstream-0.1.3.5/sample/Worker/RabbitMQ/Tasks.py +120 -0
  11. defstream-0.1.3.5/sample/Worker/RabbitMQ/Worker.py +119 -0
  12. defstream-0.1.3.5/sample/Worker/Redis/Sample.py +140 -0
  13. defstream-0.1.3.5/sample/Worker/Sample.py +237 -0
  14. {defstream-0.1.3 → defstream-0.1.3.5}/setup.py +7 -1
  15. defstream-0.1.3.5/test/test_dashboard_ui.cjs +161 -0
  16. defstream-0.1.3/DefStream.egg-info/PKG-INFO +0 -1197
  17. defstream-0.1.3/MANIFEST.in +0 -1
  18. defstream-0.1.3/PKG-INFO +0 -1197
  19. defstream-0.1.3/README.md +0 -1165
  20. defstream-0.1.3/VERSION +0 -1
  21. {defstream-0.1.3 → defstream-0.1.3.5}/DefStream.egg-info/dependency_links.txt +0 -0
  22. {defstream-0.1.3 → defstream-0.1.3.5}/DefStream.egg-info/requires.txt +0 -0
  23. {defstream-0.1.3 → defstream-0.1.3.5}/DefStream.egg-info/top_level.txt +0 -0
  24. {defstream-0.1.3 → defstream-0.1.3.5}/LICENSE +0 -0
  25. {defstream-0.1.3 → defstream-0.1.3.5}/setup.cfg +0 -0
  26. {defstream-0.1.3 → defstream-0.1.3.5}/src/Broker/Configuration.py +0 -0
  27. {defstream-0.1.3 → defstream-0.1.3.5}/src/Broker/Connection.py +0 -0
  28. {defstream-0.1.3 → defstream-0.1.3.5}/src/Broker/Consumer.py +0 -0
  29. {defstream-0.1.3 → defstream-0.1.3.5}/src/Broker/Errors.py +0 -0
  30. {defstream-0.1.3 → defstream-0.1.3.5}/src/Broker/Event.py +0 -0
  31. {defstream-0.1.3 → defstream-0.1.3.5}/src/Broker/Implements/__init__.py +0 -0
  32. {defstream-0.1.3 → defstream-0.1.3.5}/src/Broker/Policies.py +0 -0
  33. {defstream-0.1.3 → defstream-0.1.3.5}/src/Broker/Protocol.py +0 -0
  34. {defstream-0.1.3 → defstream-0.1.3.5}/src/Broker/__init__.py +0 -0
  35. {defstream-0.1.3 → defstream-0.1.3.5}/src/Descriptor/DataObject.py +0 -0
  36. {defstream-0.1.3 → defstream-0.1.3.5}/src/Descriptor/Types.py +0 -0
  37. {defstream-0.1.3 → defstream-0.1.3.5}/src/Descriptor/Value.py +0 -0
  38. {defstream-0.1.3 → defstream-0.1.3.5}/src/Descriptor/__init__.py +0 -0
  39. {defstream-0.1.3 → defstream-0.1.3.5}/src/Inspector/Extension.py +0 -0
  40. {defstream-0.1.3 → defstream-0.1.3.5}/src/Inspector/Extensions/Console.html +0 -0
  41. {defstream-0.1.3 → defstream-0.1.3.5}/src/Inspector/Extensions/Console.py +0 -0
  42. {defstream-0.1.3 → defstream-0.1.3.5}/src/Inspector/Extensions/Dashboard.html +0 -0
  43. {defstream-0.1.3 → defstream-0.1.3.5}/src/Inspector/Extensions/Dashboard.py +0 -0
  44. {defstream-0.1.3 → defstream-0.1.3.5}/src/Inspector/Extensions/HealthCheck.py +0 -0
  45. {defstream-0.1.3 → defstream-0.1.3.5}/src/Inspector/Extensions/Metrics.py +0 -0
  46. {defstream-0.1.3 → defstream-0.1.3.5}/src/Inspector/Extensions/Specifications.py +0 -0
  47. {defstream-0.1.3 → defstream-0.1.3.5}/src/Inspector/Extensions/__init__.py +0 -0
  48. {defstream-0.1.3 → defstream-0.1.3.5}/src/Inspector/Inspector.py +0 -0
  49. {defstream-0.1.3 → defstream-0.1.3.5}/src/Inspector/Response.py +0 -0
  50. {defstream-0.1.3 → defstream-0.1.3.5}/src/Inspector/__init__.py +0 -0
  51. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/Pattern.py +0 -0
  52. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/Patterns/Array.py +0 -0
  53. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/Patterns/Common.py +0 -0
  54. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/Patterns/Compare.py +0 -0
  55. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/Patterns/Condition.py +0 -0
  56. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/Patterns/DataObject.py +0 -0
  57. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/Patterns/DateTime.py +0 -0
  58. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/Patterns/Number.py +0 -0
  59. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/Patterns/Object.py +0 -0
  60. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/Patterns/Size.py +0 -0
  61. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/Patterns/String.py +0 -0
  62. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/Patterns/Type.py +0 -0
  63. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/Patterns/__init__.py +0 -0
  64. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/Validate.py +0 -0
  65. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/Validator.py +0 -0
  66. {defstream-0.1.3 → defstream-0.1.3.5}/src/Validator/__init__.py +0 -0
  67. {defstream-0.1.3 → defstream-0.1.3.5}/src/Worker/Executor.py +0 -0
  68. {defstream-0.1.3 → defstream-0.1.3.5}/src/Worker/Message.py +0 -0
  69. {defstream-0.1.3 → defstream-0.1.3.5}/src/Worker/Pool.py +0 -0
  70. {defstream-0.1.3 → defstream-0.1.3.5}/src/Worker/Run.py +0 -0
  71. {defstream-0.1.3 → defstream-0.1.3.5}/src/Worker/Specification.py +0 -0
  72. {defstream-0.1.3 → defstream-0.1.3.5}/src/Worker/Worker.py +0 -0
  73. {defstream-0.1.3 → defstream-0.1.3.5}/src/Worker/__init__.py +0 -0
  74. {defstream-0.1.3 → defstream-0.1.3.5}/src/__init__.py +0 -0
  75. {defstream-0.1.3 → defstream-0.1.3.5}/test/test_dashboard.py +0 -0
  76. {defstream-0.1.3 → defstream-0.1.3.5}/test/test_inspector.py +0 -0
  77. {defstream-0.1.3 → defstream-0.1.3.5}/test/test_inspector_console.py +0 -0
  78. {defstream-0.1.3 → defstream-0.1.3.5}/test/test_lifecycle_hooks.py +0 -0
  79. {defstream-0.1.3 → defstream-0.1.3.5}/test/test_pool_callbacks.py +0 -0
  80. {defstream-0.1.3 → defstream-0.1.3.5}/test/test_process_signals.py +0 -0
  81. {defstream-0.1.3 → defstream-0.1.3.5}/test/test_protocol_contracts.py +0 -0
  82. {defstream-0.1.3 → defstream-0.1.3.5}/test/test_runner_hooks.py +0 -0
  83. {defstream-0.1.3 → defstream-0.1.3.5}/test/test_specifications.py +0 -0
  84. {defstream-0.1.3 → defstream-0.1.3.5}/test/test_task_messages.py +0 -0
  85. {defstream-0.1.3 → defstream-0.1.3.5}/test/test_worker.py +0 -0
@@ -0,0 +1,403 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [master]
6
+ push:
7
+ branches: [master]
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ env:
13
+ RELEASE_SERIES: '0.1'
14
+
15
+ concurrency:
16
+ group: ci-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
17
+ cancel-in-progress: false
18
+
19
+ jobs:
20
+ changes:
21
+ name: Plan release version
22
+ runs-on: ubuntu-latest
23
+ timeout-minutes: 5
24
+ outputs:
25
+ release: ${{ steps.changes.outputs.release }}
26
+ version: ${{ steps.changes.outputs.version }}
27
+ steps:
28
+ - uses: actions/checkout@v7
29
+ with:
30
+ fetch-depth: 0
31
+ persist-credentials: false
32
+ - name: Set up release planner Python
33
+ uses: actions/setup-python@v7
34
+ with:
35
+ python-version: '3.13'
36
+ - name: Check complete push range and plan version
37
+ id: changes
38
+ env:
39
+ BEFORE: ${{ github.event.before }}
40
+ RELEASE_PUSH: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}
41
+ run: |
42
+ python - <<'PY'
43
+ from os import environ
44
+ from pathlib import Path
45
+ from re import fullmatch
46
+ from subprocess import check_output
47
+
48
+ if environ['RELEASE_PUSH'] != 'true':
49
+ with Path(environ['GITHUB_OUTPUT']).open('a', encoding='utf-8') as output:
50
+ output.write('release=false\n')
51
+ raise SystemExit(0)
52
+ series = environ['RELEASE_SERIES']
53
+ if fullmatch(r'(0|[1-9]\d*)\.(0|[1-9]\d*)', series) is None:
54
+ raise ValueError(f'Invalid RELEASE_SERIES: {series!r}')
55
+ before = environ['BEFORE']
56
+ if not before:
57
+ raise ValueError('Missing push base commit')
58
+ first_push = set(before) == {'0'}
59
+ base = check_output(['git', 'hash-object', '-t', 'tree', '--stdin'], input='', text=True).strip() if first_push else before
60
+ changed = check_output(
61
+ ['git', 'diff', '--name-only', '--no-renames', base, environ['GITHUB_SHA'], '--', 'src/'],
62
+ text=True,
63
+ ).strip()
64
+ # Anchor to the pre-push history so a rerun ignores its own release tag.
65
+ tags = [] if first_push else check_output(['git', 'tag', '--merged', before], text=True).splitlines()
66
+ releases = []
67
+ for tag in tags:
68
+ match = fullmatch(r'v?(\d+)\.(\d+)\.(\d+)(?:\.(\d+))?(?:\.post\d+)?', tag)
69
+ if match and f'{int(match[1])}.{int(match[2])}' == series:
70
+ releases.append(tuple(map(int, match.groups()[:3])) + (int(match[4] or 0),))
71
+ latest = max(releases) if releases else None
72
+ number = int(environ['GITHUB_RUN_NUMBER'])
73
+ if number < 1:
74
+ raise ValueError('Build number must be positive')
75
+ if latest is not None and number <= latest[3]:
76
+ raise ValueError('Build number must exceed the previous build; do not reset the workflow counter')
77
+ if changed:
78
+ patch = latest[2] + 1 if latest is not None else 0
79
+ print(f'Source changes:\n{changed}')
80
+ else:
81
+ if latest is None:
82
+ raise ValueError(
83
+ 'No release tag in pre-push history for this series. '
84
+ 'Tag the exact source commit of the existing PyPI release before retrying.'
85
+ )
86
+ patch = latest[2]
87
+ print('No src changes: keeping MAJOR.MINOR.PATCH and increasing BUILD')
88
+ version = f'{series}.{patch}.{number}'
89
+ print(f'Planned version: {version}')
90
+ with Path(environ['GITHUB_OUTPUT']).open('a', encoding='utf-8') as output:
91
+ output.write(f'release=true\nversion={version}\n')
92
+ PY
93
+
94
+ test:
95
+ name: Test Python ${{ matrix.python }}
96
+ needs: changes
97
+ runs-on: ubuntu-latest
98
+ timeout-minutes: 25
99
+ strategy:
100
+ fail-fast: false
101
+ matrix:
102
+ python: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
103
+ env:
104
+ PROJECT: ${{ github.event.repository.name }}
105
+ DEFSTREAM_RABBITMQ_TESTS: '1'
106
+ DEFSTREAM_REDIS_TESTS: '1'
107
+ services:
108
+ rabbitmq:
109
+ image: rabbitmq:4.1
110
+ ports:
111
+ - 5672:5672
112
+ env:
113
+ RABBITMQ_DEFAULT_USER: guest
114
+ RABBITMQ_DEFAULT_PASS: guest
115
+ RABBITMQ_SERVER_ADDITIONAL_ERL_ARGS: '-rabbit loopback_users []'
116
+ options: >-
117
+ --health-cmd "rabbitmq-diagnostics -q ping"
118
+ --health-interval 10s
119
+ --health-timeout 10s
120
+ --health-retries 12
121
+ redis:
122
+ image: redis:7.4
123
+ ports:
124
+ - 6379:6379
125
+ options: >-
126
+ --health-cmd "redis-cli ping"
127
+ --health-interval 5s
128
+ --health-timeout 5s
129
+ --health-retries 12
130
+ steps:
131
+ - name: Check out core
132
+ uses: actions/checkout@v7
133
+ with:
134
+ repository: yong5eon/DefStream
135
+ ref: ${{ github.event.repository.name == 'DefStream' && github.sha || 'master' }}
136
+ path: packages/DefStream
137
+ fetch-depth: 0
138
+ persist-credentials: false
139
+
140
+ - name: Check out RabbitMQ adapter
141
+ uses: actions/checkout@v7
142
+ with:
143
+ repository: yong5eon/DefStream-RabbitMQ
144
+ ref: ${{ github.event.repository.name == 'DefStream-RabbitMQ' && github.sha || 'master' }}
145
+ path: packages/DefStream-RabbitMQ
146
+ fetch-depth: 0
147
+ persist-credentials: false
148
+
149
+ - name: Check out Redis adapter
150
+ uses: actions/checkout@v7
151
+ with:
152
+ repository: yong5eon/DefStream-Redis
153
+ ref: ${{ github.event.repository.name == 'DefStream-Redis' && github.sha || 'master' }}
154
+ path: packages/DefStream-Redis
155
+ fetch-depth: 0
156
+ persist-credentials: false
157
+
158
+ - name: Set up Python
159
+ uses: actions/setup-python@v7
160
+ with:
161
+ python-version: ${{ matrix.python }}
162
+
163
+ - name: Record source commits and tag candidate locally
164
+ env:
165
+ RELEASE_BUILD: ${{ needs.changes.outputs.release }}
166
+ RELEASE_VERSION: ${{ needs.changes.outputs.version }}
167
+ run: |
168
+ for package in packages/*; do
169
+ echo "$package: $(git -C "$package" rev-parse HEAD)"
170
+ done
171
+ echo "SOURCE_DATE_EPOCH=$(git -C "packages/$PROJECT" show -s --format=%ct HEAD)" >> "$GITHUB_ENV"
172
+ python - <<'PY'
173
+ from os import environ
174
+ from pathlib import Path
175
+ from re import fullmatch
176
+ from subprocess import check_output, run
177
+
178
+ project = environ['PROJECT']
179
+ if project not in ('DefStream', 'DefStream-RabbitMQ', 'DefStream-Redis'):
180
+ raise ValueError(f'Unexpected project: {project}')
181
+ if environ['RELEASE_BUILD'] != 'true':
182
+ print('Test-only build: using the existing Git-derived development version.')
183
+ raise SystemExit(0)
184
+ version = environ['RELEASE_VERSION']
185
+ if fullmatch(r'(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)\.[1-9]\d*', version) is None:
186
+ raise ValueError(f'Invalid planned version: {version!r}')
187
+ path = Path('packages') / project
188
+ tag = version
189
+ tags = check_output(['git', '-C', str(path), 'tag', '--list', tag], text=True).strip()
190
+ if tags:
191
+ tagged = check_output(['git', '-C', str(path), 'rev-parse', f'{tag}^{{commit}}'], text=True).strip()
192
+ if tagged != environ['GITHUB_SHA']:
193
+ raise RuntimeError(f'{tag} already points at another commit')
194
+ else:
195
+ run(['git', '-C', str(path), 'tag', tag, environ['GITHUB_SHA']], check=True)
196
+ print(f'{project}: local candidate {tag}')
197
+ with Path(environ['GITHUB_STEP_SUMMARY']).open('a', encoding='utf-8') as summary:
198
+ summary.write(f'## {project} {version}\n\nSource: `{environ["GITHUB_SHA"]}`\n')
199
+ PY
200
+
201
+ - name: Install build tools
202
+ run: python -m pip install --upgrade build twine
203
+
204
+ - name: Build source distributions and wheels
205
+ run: |
206
+ mkdir -p dist dependency-wheels
207
+ for name in DefStream DefStream-RabbitMQ DefStream-Redis; do
208
+ if [ "$name" = "$PROJECT" ]; then
209
+ python -m build --outdir "$GITHUB_WORKSPACE/dist" "packages/$name"
210
+ else
211
+ python -m build --outdir "$GITHUB_WORKSPACE/dependency-wheels" "packages/$name"
212
+ fi
213
+ done
214
+ python -m twine check --strict dist/*
215
+
216
+ - name: Install built wheels into a clean environment
217
+ run: |
218
+ python -m venv "$RUNNER_TEMP/installed"
219
+ "$RUNNER_TEMP/installed/bin/python" -m pip install dist/*.whl dependency-wheels/*.whl
220
+ "$RUNNER_TEMP/installed/bin/python" -m pip check
221
+
222
+ - name: Test installed package
223
+ run: |
224
+ cd "$RUNNER_TEMP"
225
+ "$RUNNER_TEMP/installed/bin/python" -m unittest discover \
226
+ -s "$GITHUB_WORKSPACE/packages/$PROJECT/test" -p 'test_*.py' -v
227
+
228
+ - name: Set up Node for core UI tests
229
+ if: env.PROJECT == 'DefStream'
230
+ uses: actions/setup-node@v7
231
+ with:
232
+ node-version: '22'
233
+
234
+ - name: Test core UI
235
+ if: env.PROJECT == 'DefStream'
236
+ run: node --test packages/DefStream/test/test_dashboard_ui.cjs
237
+
238
+ - name: Preserve verified release distributions
239
+ if: needs.changes.outputs.release == 'true' && matrix.python == '3.13'
240
+ uses: actions/upload-artifact@v7
241
+ with:
242
+ name: distributions
243
+ path: dist/*
244
+ if-no-files-found: error
245
+ retention-days: 14
246
+
247
+ publish:
248
+ name: Publish PyPI
249
+ if: github.event_name == 'push' && github.ref == 'refs/heads/master' && needs.changes.outputs.release == 'true'
250
+ needs: [changes, test]
251
+ runs-on: ubuntu-latest
252
+ timeout-minutes: 10
253
+ environment: pypi
254
+ permissions:
255
+ contents: write
256
+ steps:
257
+ - name: Check out release source for tagging
258
+ uses: actions/checkout@v7
259
+ with:
260
+ ref: ${{ github.sha }}
261
+ path: release-source
262
+ fetch-depth: 0
263
+
264
+ - name: Download verified distributions
265
+ uses: actions/download-artifact@v8
266
+ with:
267
+ name: distributions
268
+ path: dist
269
+
270
+ - name: Set up Python
271
+ uses: actions/setup-python@v7
272
+ with:
273
+ python-version: '3.13'
274
+
275
+ - name: Check release status
276
+ id: release
277
+ env:
278
+ PROJECT: ${{ github.event.repository.name }}
279
+ RELEASE_VERSION: ${{ needs.changes.outputs.version }}
280
+ run: |
281
+ python - <<'PY'
282
+ from email.parser import BytesParser
283
+ from json import load
284
+ from os import environ
285
+ from pathlib import Path
286
+ from urllib.error import HTTPError
287
+ from urllib.request import urlopen
288
+ from zipfile import ZipFile
289
+
290
+ wheels = list(Path('dist').glob('*.whl'))
291
+ sources = list(Path('dist').glob('*.tar.gz'))
292
+ if len(wheels) != 1 or len(sources) != 1:
293
+ raise ValueError('Expected exactly one wheel and one source distribution')
294
+ with ZipFile(wheels[0]) as archive:
295
+ metadata_path, = (name for name in archive.namelist() if name.endswith('.dist-info/METADATA'))
296
+ metadata = BytesParser().parsebytes(archive.read(metadata_path))
297
+ project = environ['PROJECT']
298
+ if metadata['Name'] != project:
299
+ raise ValueError(f'Unexpected distribution: {metadata["Name"]}')
300
+ version = metadata['Version']
301
+ expected_version = environ['RELEASE_VERSION']
302
+ if version != expected_version:
303
+ raise ValueError(f'Expected {expected_version}, got {version}')
304
+ try:
305
+ with urlopen(f'https://pypi.org/pypi/{project}/{version}/json', timeout=30) as response:
306
+ release = load(response)
307
+ except HTTPError as error:
308
+ if error.code != 404:
309
+ raise
310
+ exists = False
311
+ else:
312
+ expected = {wheels[0].name, sources[0].name}
313
+ published = {file['filename'] for file in release['urls']}
314
+ if int(environ['GITHUB_RUN_ATTEMPT']) == 1 or not expected.issubset(published):
315
+ raise RuntimeError(
316
+ f'{project} {version} already exists or is partially published. '
317
+ 'Do not overwrite it; use a new run/version or repair the release manually.'
318
+ )
319
+ print(f'{project} {version}: already published; skipping this rerun')
320
+ exists = True
321
+ with Path(environ['GITHUB_OUTPUT']).open('a', encoding='utf-8') as output:
322
+ output.write(f'publish={str(not exists).lower()}\n')
323
+ output.write(f'version={version}\n')
324
+ PY
325
+
326
+ - name: Record verified release tag
327
+ env:
328
+ VERSION: ${{ steps.release.outputs.version }}
329
+ run: |
330
+ tag="$VERSION"
331
+ cd release-source
332
+ if git show-ref --verify --quiet "refs/tags/$tag"; then
333
+ if [ "$(git rev-parse "$tag^{commit}")" != "$GITHUB_SHA" ]; then
334
+ echo "::error::$tag already points at another commit."
335
+ exit 1
336
+ fi
337
+ else
338
+ git tag "$tag" "$GITHUB_SHA"
339
+ fi
340
+ git push origin "refs/tags/$tag"
341
+
342
+ - name: Install upload tool
343
+ if: steps.release.outputs.publish == 'true'
344
+ run: python -m pip install --upgrade twine
345
+
346
+ - name: Upload to PyPI
347
+ if: steps.release.outputs.publish == 'true'
348
+ env:
349
+ TWINE_USERNAME: __token__
350
+ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
351
+ run: |
352
+ if [ -z "$TWINE_PASSWORD" ]; then
353
+ echo "::error::Configure PYPI_API_TOKEN in the pypi environment or repository Actions secrets."
354
+ exit 1
355
+ fi
356
+ python -m twine upload --non-interactive dist/*
357
+
358
+ - name: Publish GitHub Release with verified distributions
359
+ env:
360
+ GH_TOKEN: ${{ github.token }}
361
+ VERSION: ${{ steps.release.outputs.version }}
362
+ run: |
363
+ python - <<'PY'
364
+ from json import load
365
+ from os import environ
366
+ from pathlib import Path
367
+ from subprocess import run
368
+ from urllib.error import HTTPError
369
+ from urllib.parse import quote
370
+ from urllib.request import Request, urlopen
371
+
372
+ repository = environ['GITHUB_REPOSITORY']
373
+ version = environ['VERSION']
374
+ request = Request(
375
+ f'{environ["GITHUB_API_URL"]}/repos/{repository}/releases/tags/{quote(version, safe="")}',
376
+ headers={
377
+ 'Authorization': f'Bearer {environ["GH_TOKEN"]}',
378
+ 'Accept': 'application/vnd.github+json',
379
+ 'X-GitHub-Api-Version': '2022-11-28',
380
+ },
381
+ )
382
+ try:
383
+ with urlopen(request, timeout=30) as response:
384
+ release = load(response)
385
+ except HTTPError as error:
386
+ if error.code != 404:
387
+ raise
388
+ run([
389
+ 'gh', 'release', 'create', version, '--repo', repository,
390
+ '--verify-tag', '--title', version, '--generate-notes', '--draft',
391
+ ], check=True)
392
+ else:
393
+ if release['tag_name'] != version:
394
+ raise ValueError('GitHub Release does not match the verified version')
395
+ artifacts = sorted(str(path) for path in Path('dist').iterdir() if path.is_file())
396
+ run([
397
+ 'gh', 'release', 'upload', version, '--repo', repository,
398
+ '--clobber', *artifacts,
399
+ ], check=True)
400
+ run([
401
+ 'gh', 'release', 'edit', version, '--repo', repository, '--draft=false',
402
+ ], check=True)
403
+ PY
@@ -0,0 +1,62 @@
1
+ # Python bytecode and caches
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+ .pytest_cache/
6
+ .mypy_cache/
7
+ .ruff_cache/
8
+ .tox/
9
+ .nox/
10
+ .coverage
11
+ .coverage.*
12
+
13
+ # Virtual environments and local environment files
14
+ .venv/
15
+ venv/
16
+ env/
17
+ .env
18
+ .env.*
19
+
20
+ # Python packaging and build artifacts
21
+ build/
22
+ dist/
23
+ *.egg-info/
24
+ *.egg
25
+ .Python
26
+
27
+ # Local broker adapter links
28
+ /src/Broker/Implements/*
29
+ !/src/Broker/Implements/__init__.py
30
+
31
+ # Operating system files
32
+ .DS_Store
33
+ Thumbs.db
34
+ Desktop.ini
35
+
36
+ # Miscellaneous files
37
+ *.log
38
+ *.tmp
39
+ *.bak
40
+ *.swp
41
+ *.swo
42
+
43
+ # Temporary files
44
+ tmp/
45
+ *.tmp
46
+ *.temp
47
+
48
+ # IDE and editor specific files
49
+ .vscode/
50
+ .idea/
51
+
52
+ # Git
53
+ .git/
54
+
55
+ # GitHub specific files
56
+ .github/
57
+ !/.github/
58
+ /.github/*
59
+ !/.github/workflows/
60
+
61
+ # GitLab specific files
62
+ .gitlab/