rust-py-audit 0.3.1__tar.gz → 0.3.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 (47) hide show
  1. rust_py_audit-0.3.2/.github/workflows/release.yml +208 -0
  2. rust_py_audit-0.3.2/CHANGELOG.md +50 -0
  3. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/Cargo.lock +1 -1
  4. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/Cargo.toml +1 -1
  5. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/PKG-INFO +1 -1
  6. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/pyproject.toml +1 -1
  7. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/python/rust_py_audit/__init__.py +1 -1
  8. rust_py_audit-0.3.1/.github/workflows/release.yml +0 -117
  9. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/.gitignore +0 -0
  10. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/LICENSE +0 -0
  11. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/README.md +0 -0
  12. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/ROADMAP.md +0 -0
  13. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/examples/__init__.py +0 -0
  14. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/examples/basic_usage.py +0 -0
  15. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/examples/django_example/__init__.py +0 -0
  16. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/examples/django_example/settings.py +0 -0
  17. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/examples/django_example/urls.py +0 -0
  18. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/examples/django_example/views.py +0 -0
  19. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/examples/fastapi_app.py +0 -0
  20. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/examples/immutablelog_basic.py +0 -0
  21. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/python/rust_py_audit/django.py +0 -0
  22. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/python/rust_py_audit/fastapi.py +0 -0
  23. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/src/audit_logger.rs +0 -0
  24. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/src/errors.rs +0 -0
  25. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/src/event.rs +0 -0
  26. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/src/hash.rs +0 -0
  27. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/src/immutablelog_client.rs +0 -0
  28. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/src/immutablelog_config.rs +0 -0
  29. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/src/immutablelog_receipt.rs +0 -0
  30. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/src/lib.rs +0 -0
  31. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/src/pyconv.rs +0 -0
  32. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/src/retry.rs +0 -0
  33. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/src/storage.rs +0 -0
  34. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/src/verifier.rs +0 -0
  35. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/tests/conftest.py +0 -0
  36. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/tests/test_audit_logger.py +0 -0
  37. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/tests/test_concurrency.py +0 -0
  38. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/tests/test_django_immutablelog.py +0 -0
  39. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/tests/test_django_middleware.py +0 -0
  40. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/tests/test_fastapi_immutablelog.py +0 -0
  41. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/tests/test_fastapi_middleware.py +0 -0
  42. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/tests/test_flush_pending.py +0 -0
  43. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/tests/test_hello.py +0 -0
  44. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/tests/test_immutablelog_hybrid.py +0 -0
  45. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/tests/test_immutablelog_remote.py +0 -0
  46. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/tests/test_metadata_bigint.py +0 -0
  47. {rust_py_audit-0.3.1 → rust_py_audit-0.3.2}/tests/test_verify.py +0 -0
@@ -0,0 +1,208 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+ workflow_dispatch: {}
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ jobs:
13
+ # Nothing gets built, and nothing reaches PyPI, until this passes. Publishing
14
+ # to PyPI is irreversible: a bad wheel cannot be replaced, only yanked.
15
+ test:
16
+ name: Test (Rust + Python)
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: dtolnay/rust-toolchain@stable
21
+ - uses: actions/setup-python@v5
22
+ with:
23
+ python-version: "3.12"
24
+
25
+ # --no-default-features drops extension-module, which tells the linker to
26
+ # leave libpython symbols undefined. That is correct for the wheel but
27
+ # produces a test binary that cannot link on Linux.
28
+ - name: Rust unit tests
29
+ env:
30
+ PYO3_PYTHON: python
31
+ run: cargo test --no-default-features --lib
32
+
33
+ - name: Build, install the extension and run the Python tests
34
+ # `pip install` drives the maturin build backend, so it compiles the
35
+ # Rust extension and installs it (plus the dev extras) into the
36
+ # runner's Python — no virtualenv needed, unlike `maturin develop`,
37
+ # which aborts when it cannot find one.
38
+ run: |
39
+ python -m pip install --upgrade pip
40
+ python -m pip install ".[dev]"
41
+ python -m pytest
42
+
43
+ linux:
44
+ runs-on: ubuntu-latest
45
+ needs: test
46
+ strategy:
47
+ matrix:
48
+ target: [x86_64, aarch64]
49
+ steps:
50
+ - uses: actions/checkout@v4
51
+ - uses: PyO3/maturin-action@v1
52
+ env:
53
+ # `ring` (puxado por `rustls`, via `reqwest`) falha ao cross-compilar
54
+ # sua assembly pregenerada para aarch64 dentro do container manylinux
55
+ # do maturin-action: o cross-gcc não define `__ARM_ARCH` por padrão
56
+ # nesse ambiente, e o preprocessor de `ring` exige essa macro
57
+ # (erro: "ARM assembler must define __ARM_ARCH"). Forçar a macro via
58
+ # CFLAGS é o workaround padrão para esse problema conhecido — não
59
+ # afeta o build x86_64 (a env var é específica do target aarch64).
60
+ CFLAGS_aarch64_unknown_linux_gnu: -D__ARM_ARCH=8
61
+ with:
62
+ target: ${{ matrix.target }}
63
+ args: --release --out dist --find-interpreter
64
+ manylinux: auto
65
+ - uses: actions/upload-artifact@v4
66
+ with:
67
+ name: wheels-linux-${{ matrix.target }}
68
+ path: dist
69
+
70
+ musllinux:
71
+ runs-on: ubuntu-latest
72
+ needs: test
73
+ strategy:
74
+ matrix:
75
+ target: [x86_64, aarch64]
76
+ steps:
77
+ - uses: actions/checkout@v4
78
+ - uses: PyO3/maturin-action@v1
79
+ with:
80
+ target: ${{ matrix.target }}
81
+ args: --release --out dist --find-interpreter
82
+ manylinux: musllinux_1_2
83
+ - uses: actions/upload-artifact@v4
84
+ with:
85
+ name: wheels-musllinux-${{ matrix.target }}
86
+ path: dist
87
+
88
+ windows:
89
+ runs-on: windows-latest
90
+ needs: test
91
+ steps:
92
+ - uses: actions/checkout@v4
93
+ - uses: actions/setup-python@v5
94
+ with:
95
+ architecture: x64
96
+ - uses: PyO3/maturin-action@v1
97
+ with:
98
+ target: x64
99
+ args: --release --out dist --find-interpreter
100
+ - uses: actions/upload-artifact@v4
101
+ with:
102
+ name: wheels-windows-x64
103
+ path: dist
104
+
105
+ macos:
106
+ runs-on: macos-latest
107
+ needs: test
108
+ strategy:
109
+ matrix:
110
+ target: [x86_64, aarch64]
111
+ steps:
112
+ - uses: actions/checkout@v4
113
+ - uses: PyO3/maturin-action@v1
114
+ with:
115
+ target: ${{ matrix.target }}
116
+ args: --release --out dist --find-interpreter
117
+ - uses: actions/upload-artifact@v4
118
+ with:
119
+ name: wheels-macos-${{ matrix.target }}
120
+ path: dist
121
+
122
+ sdist:
123
+ runs-on: ubuntu-latest
124
+ needs: test
125
+ steps:
126
+ - uses: actions/checkout@v4
127
+ - uses: PyO3/maturin-action@v1
128
+ with:
129
+ command: sdist
130
+ args: --out dist
131
+ - uses: actions/upload-artifact@v4
132
+ with:
133
+ name: wheels-sdist
134
+ path: dist
135
+
136
+ release:
137
+ name: Publish to PyPI
138
+ runs-on: ubuntu-latest
139
+ needs: [test, linux, musllinux, windows, macos, sdist]
140
+ steps:
141
+ - uses: actions/download-artifact@v4
142
+ with:
143
+ pattern: wheels-*
144
+ path: dist
145
+ merge-multiple: true
146
+ - uses: actions/setup-python@v5
147
+ with:
148
+ python-version: "3.12"
149
+ - name: Install maturin
150
+ run: python -m pip install --upgrade maturin requests
151
+
152
+ # A sanity check before anything leaves the runner: publishing an
153
+ # incomplete set is worse than not publishing, because PyPI never lets a
154
+ # filename be reused — a half-uploaded version can only be completed,
155
+ # never replaced.
156
+ - name: Verify the artifact set is complete
157
+ run: |
158
+ set -euo pipefail
159
+ ls -la dist/
160
+ test -n "$(find dist -name '*.tar.gz' -print -quit)" \
161
+ || { echo "::error::sdist ausente — abortando antes de publicar"; exit 1; }
162
+ wheels=$(find dist -name '*.whl' | wc -l)
163
+ echo "wheels: $wheels"
164
+ test "$wheels" -ge 7 \
165
+ || { echo "::error::só $wheels wheels; esperado >= 7 (2 linux, 2 musl, 1 win, 2 macos)"; exit 1; }
166
+
167
+ # PyPI returns a transient 500 often enough to matter, and the upload is
168
+ # file-by-file: a failure halfway leaves the version published in part.
169
+ # --skip-existing makes the retry idempotent (already-uploaded files are
170
+ # skipped by name), so a second attempt finishes the job instead of
171
+ # failing on the files that did make it.
172
+ - name: Publish to PyPI (with retry)
173
+ uses: nick-fields/retry@v3
174
+ env:
175
+ MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
176
+ with:
177
+ timeout_minutes: 15
178
+ max_attempts: 4
179
+ retry_wait_seconds: 45
180
+ command: maturin upload --non-interactive --skip-existing dist/*
181
+
182
+ # Proves the release is actually complete, rather than trusting an exit
183
+ # code that a partial upload can still produce.
184
+ - name: Verify every file reached PyPI
185
+ run: |
186
+ set -euo pipefail
187
+ python - <<'PY'
188
+ import os, sys, glob, time, requests
189
+ version = os.environ["GITHUB_REF_NAME"].lstrip("v")
190
+ local = {os.path.basename(p) for p in glob.glob("dist/*")}
191
+ url = f"https://pypi.org/pypi/rust-py-audit/{version}/json"
192
+ for attempt in range(6): # PyPI's JSON API is cached
193
+ response = requests.get(url, timeout=30)
194
+ if response.status_code == 200:
195
+ remote = {f["filename"] for f in response.json()["urls"]}
196
+ missing = local - remote
197
+ if not missing:
198
+ print(f"OK: {len(remote)} arquivos publicados na {version}")
199
+ sys.exit(0)
200
+ else:
201
+ missing = local
202
+ print(f"tentativa {attempt + 1}: faltam {len(missing)}")
203
+ time.sleep(20)
204
+ print("::error::faltaram arquivos no PyPI:")
205
+ for name in sorted(missing):
206
+ print(f" - {name}")
207
+ sys.exit(1)
208
+ PY
@@ -0,0 +1,50 @@
1
+ # Changelog
2
+
3
+ Todas as mudanças relevantes deste projeto são registradas aqui.
4
+
5
+ O formato segue [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/),
6
+ e o versionamento segue [SemVer](https://semver.org/lang/pt-BR/).
7
+
8
+ ## [0.3.2] - 2026-08-11
9
+
10
+ Release de infraestrutura: nada muda no comportamento da biblioteca.
11
+
12
+ ### Corrigido
13
+
14
+ - **A publicação não roda mais sem teste.** O `release.yml` era o único
15
+ workflow do repositório e só tinha jobs de build: `git push --tags`
16
+ publicava no PyPI sem nenhum `cargo test` nem `pytest`. Agora existe um job
17
+ `test` e todos os jobs de wheel, o de sdist e o de release declaram
18
+ `needs: test`. (BACKLOG2 P1-1)
19
+ - **O upload aguenta o 500 do PyPI.** O upload é arquivo por arquivo e sem
20
+ transação — uma falha no meio deixa a versão publicada pela metade, e o PyPI
21
+ nunca permite reusar um nome de arquivo. O passo de publicação agora tem
22
+ retry (4 tentativas), continua usando `--skip-existing` para ser idempotente,
23
+ confere o conjunto de artefatos **antes** de subir qualquer coisa (aborta se
24
+ faltar o sdist) e valida contra a API do PyPI **depois**, listando nome por
25
+ nome o que não chegou. (BACKLOG2 P1-9)
26
+
27
+ ### Adicionado
28
+
29
+ - Este `CHANGELOG.md`. (BACKLOG2 P1-5)
30
+
31
+ ## [0.3.1] e anteriores
32
+
33
+ Sem changelog retroativo. O histórico do que entrou em cada versão está no
34
+ `ROADMAP.md`, e o das mudanças no `git log`.
35
+
36
+ Resumo do que o projeto entrega hoje:
37
+
38
+ - `AuditLogger` com log append-only em JSONL e cadeia de hashes encadeada.
39
+ - `verify()` para detectar adulteração da cadeia.
40
+ - Integração com o ImmutableLog nos modos remoto e híbrido.
41
+ - Middlewares para FastAPI e Django.
42
+ - Wheels `abi3-py310` — uma wheel por plataforma, válida da 3.10 em diante.
43
+
44
+ ### Limitação conhecida (não corrigida nesta versão)
45
+
46
+ ⚠️ **Múltiplos processos escrevendo no mesmo arquivo quebram a cadeia.** Cada
47
+ processo mantém seu próprio `last_hash` em memória e não há `flock` no append,
48
+ então dois workers gunicorn apontando para o mesmo JSONL produzem uma cadeia
49
+ inconsistente — e `verify()` acusa corrupção. O modo de falha é silencioso até
50
+ alguém rodar `verify()`. Ver **P2-4** no `BACKLOG2.md`.
@@ -1597,7 +1597,7 @@ dependencies = [
1597
1597
 
1598
1598
  [[package]]
1599
1599
  name = "rust_py_audit"
1600
- version = "0.3.1"
1600
+ version = "0.3.2"
1601
1601
  dependencies = [
1602
1602
  "httpmock",
1603
1603
  "pyo3",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "rust_py_audit"
3
- version = "0.3.1"
3
+ version = "0.3.2"
4
4
  edition = "2021"
5
5
  description = "Auditoria de eventos com cadeia de hashes (SHA-256) para apps Python, core em Rust via PyO3."
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rust-py-audit
3
- Version: 0.3.1
3
+ Version: 0.3.2
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "rust-py-audit"
7
- version = "0.3.1"
7
+ version = "0.3.2"
8
8
  description = "Fast, tamper-evident event auditing (hash chain) for Python apps, with a Rust core."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -8,5 +8,5 @@ que existe um módulo nativo por trás.
8
8
 
9
9
  from ._rust_py_audit import AuditLogger, hello
10
10
 
11
- __version__ = "0.3.1"
11
+ __version__ = "0.3.2"
12
12
  __all__ = ["AuditLogger", "hello"]
@@ -1,117 +0,0 @@
1
- name: Release
2
-
3
- on:
4
- push:
5
- tags:
6
- - "v*"
7
- workflow_dispatch: {}
8
-
9
- permissions:
10
- contents: read
11
-
12
- jobs:
13
- linux:
14
- runs-on: ubuntu-latest
15
- strategy:
16
- matrix:
17
- target: [x86_64, aarch64]
18
- steps:
19
- - uses: actions/checkout@v4
20
- - uses: PyO3/maturin-action@v1
21
- env:
22
- # `ring` (puxado por `rustls`, via `reqwest`) falha ao cross-compilar
23
- # sua assembly pregenerada para aarch64 dentro do container manylinux
24
- # do maturin-action: o cross-gcc não define `__ARM_ARCH` por padrão
25
- # nesse ambiente, e o preprocessor de `ring` exige essa macro
26
- # (erro: "ARM assembler must define __ARM_ARCH"). Forçar a macro via
27
- # CFLAGS é o workaround padrão para esse problema conhecido — não
28
- # afeta o build x86_64 (a env var é específica do target aarch64).
29
- CFLAGS_aarch64_unknown_linux_gnu: -D__ARM_ARCH=8
30
- with:
31
- target: ${{ matrix.target }}
32
- args: --release --out dist --find-interpreter
33
- manylinux: auto
34
- - uses: actions/upload-artifact@v4
35
- with:
36
- name: wheels-linux-${{ matrix.target }}
37
- path: dist
38
-
39
- musllinux:
40
- runs-on: ubuntu-latest
41
- strategy:
42
- matrix:
43
- target: [x86_64, aarch64]
44
- steps:
45
- - uses: actions/checkout@v4
46
- - uses: PyO3/maturin-action@v1
47
- with:
48
- target: ${{ matrix.target }}
49
- args: --release --out dist --find-interpreter
50
- manylinux: musllinux_1_2
51
- - uses: actions/upload-artifact@v4
52
- with:
53
- name: wheels-musllinux-${{ matrix.target }}
54
- path: dist
55
-
56
- windows:
57
- runs-on: windows-latest
58
- steps:
59
- - uses: actions/checkout@v4
60
- - uses: actions/setup-python@v5
61
- with:
62
- architecture: x64
63
- - uses: PyO3/maturin-action@v1
64
- with:
65
- target: x64
66
- args: --release --out dist --find-interpreter
67
- - uses: actions/upload-artifact@v4
68
- with:
69
- name: wheels-windows-x64
70
- path: dist
71
-
72
- macos:
73
- runs-on: macos-latest
74
- strategy:
75
- matrix:
76
- target: [x86_64, aarch64]
77
- steps:
78
- - uses: actions/checkout@v4
79
- - uses: PyO3/maturin-action@v1
80
- with:
81
- target: ${{ matrix.target }}
82
- args: --release --out dist --find-interpreter
83
- - uses: actions/upload-artifact@v4
84
- with:
85
- name: wheels-macos-${{ matrix.target }}
86
- path: dist
87
-
88
- sdist:
89
- runs-on: ubuntu-latest
90
- steps:
91
- - uses: actions/checkout@v4
92
- - uses: PyO3/maturin-action@v1
93
- with:
94
- command: sdist
95
- args: --out dist
96
- - uses: actions/upload-artifact@v4
97
- with:
98
- name: wheels-sdist
99
- path: dist
100
-
101
- release:
102
- name: Publish to PyPI
103
- runs-on: ubuntu-latest
104
- needs: [linux, musllinux, windows, macos, sdist]
105
- steps:
106
- - uses: actions/download-artifact@v4
107
- with:
108
- pattern: wheels-*
109
- path: dist
110
- merge-multiple: true
111
- - name: Publish to PyPI
112
- uses: PyO3/maturin-action@v1
113
- env:
114
- MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
115
- with:
116
- command: upload
117
- args: --non-interactive --skip-existing dist/*
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes