pysentry-rs 0.3.11__tar.gz → 0.3.12__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.
Potentially problematic release.
This version of pysentry-rs might be problematic. Click here for more details.
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/.github/workflows/ci.yml +1 -1
- pysentry_rs-0.3.12/.github/workflows/dev-release-test.yml +213 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/.github/workflows/release.yml +20 -64
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/Cargo.lock +1 -1
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/Cargo.toml +1 -1
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/PKG-INFO +16 -13
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/README.md +15 -12
- pysentry_rs-0.3.12/benchmarks/results/0.3.11.md +141 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/config.rs +86 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/parsers/requirements.rs +258 -1
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/python.rs +11 -5
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/.github/FUNDING.yml +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/.github/dependabot.yml +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/.github/workflows/benchmark.yml +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/.gitignore +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/.pre-commit-config.yaml +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/.pre-commit-hooks.yaml +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/LICENSE +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/.gitignore +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/.python-version +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/README.md +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/main.py +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/pyproject.toml +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/results/0.2.3.md +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/results/0.3.1.md +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/results/0.3.10.md +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/results/0.3.2.md +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/results/0.3.3.md +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/results/0.3.4.md +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/results/0.3.5.md +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/results/0.3.6.md +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/results/0.3.7.md +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/results/latest.md +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/src/benchmark_runner.py +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/src/performance_monitor.py +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/src/report_generator.py +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/src/tool_wrapper.py +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/test_data/large_requirements.txt +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/test_data/small_requirements.txt +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/test_data/uv.lock +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/benchmarks/uv.lock +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/fixtures/pipfile-tests/Pipfile +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/fixtures/pipfile-tests/Pipfile.lock +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/fixtures/pipfile-vulnerable-tests/Pipfile +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/fixtures/requirements-tests/requirements-dev.txt +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/fixtures/requirements-tests/requirements.txt +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/fixtures/requirements-tests-vulnerable/requirements.txt +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/pyproject.toml +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/python/pysentry/__init__.py +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/cache/audit.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/cache/mod.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/cache/storage.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/cli.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/dependency/mod.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/dependency/resolvers/mod.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/dependency/resolvers/pip_tools.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/dependency/resolvers/uv.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/dependency/scanner.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/error.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/lib.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/main.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/output/mod.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/output/report.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/output/sarif.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/parsers/lock.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/parsers/mod.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/parsers/pipfile.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/parsers/pipfile_lock.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/parsers/poetry_lock.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/parsers/pylock.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/parsers/pyproject.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/providers/mod.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/providers/osv.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/providers/pypa.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/providers/pypi.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/providers/retry.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/types.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/vulnerability/database.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/vulnerability/matcher.rs +0 -0
- {pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/src/vulnerability/mod.rs +0 -0
|
@@ -249,7 +249,7 @@ jobs:
|
|
|
249
249
|
strategy:
|
|
250
250
|
matrix:
|
|
251
251
|
os: [ubuntu-latest, macos-latest, windows-latest]
|
|
252
|
-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
|
|
252
|
+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
253
253
|
|
|
254
254
|
steps:
|
|
255
255
|
- uses: actions/checkout@v4
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
name: Dev Release Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
|
|
6
|
+
env:
|
|
7
|
+
CARGO_TERM_COLOR: always
|
|
8
|
+
RUST_BACKTRACE: 1
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test-build-binaries:
|
|
12
|
+
name: Test Build Binaries
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
include:
|
|
17
|
+
- target: x86_64-unknown-linux-gnu
|
|
18
|
+
os: ubuntu-latest
|
|
19
|
+
name: linux-x64
|
|
20
|
+
artifact_name: pysentry
|
|
21
|
+
- target: x86_64-unknown-linux-musl
|
|
22
|
+
os: ubuntu-latest
|
|
23
|
+
name: linux-x64-musl
|
|
24
|
+
artifact_name: pysentry
|
|
25
|
+
- target: aarch64-unknown-linux-gnu
|
|
26
|
+
os: ubuntu-latest
|
|
27
|
+
name: linux-arm64
|
|
28
|
+
artifact_name: pysentry
|
|
29
|
+
- target: x86_64-apple-darwin
|
|
30
|
+
os: macos-latest
|
|
31
|
+
name: macos-x64
|
|
32
|
+
artifact_name: pysentry
|
|
33
|
+
- target: aarch64-apple-darwin
|
|
34
|
+
os: macos-latest
|
|
35
|
+
name: macos-arm64
|
|
36
|
+
artifact_name: pysentry
|
|
37
|
+
- target: x86_64-pc-windows-msvc
|
|
38
|
+
os: windows-latest
|
|
39
|
+
name: windows-x64
|
|
40
|
+
artifact_name: pysentry.exe
|
|
41
|
+
|
|
42
|
+
runs-on: ${{ matrix.os }}
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/checkout@v4
|
|
45
|
+
|
|
46
|
+
- name: Install Rust
|
|
47
|
+
uses: dtolnay/rust-toolchain@stable
|
|
48
|
+
with:
|
|
49
|
+
targets: ${{ matrix.target }}
|
|
50
|
+
|
|
51
|
+
- name: Install cross
|
|
52
|
+
if: matrix.target != 'x86_64-unknown-linux-gnu'
|
|
53
|
+
run: cargo install cross
|
|
54
|
+
|
|
55
|
+
- name: Build binary (native)
|
|
56
|
+
if: matrix.target == 'x86_64-unknown-linux-gnu'
|
|
57
|
+
run: cargo build --release --target ${{ matrix.target }} --bin pysentry
|
|
58
|
+
|
|
59
|
+
- name: Build binary (cross-compile)
|
|
60
|
+
if: matrix.target != 'x86_64-unknown-linux-gnu'
|
|
61
|
+
run: cross build --release --target ${{ matrix.target }} --bin pysentry
|
|
62
|
+
|
|
63
|
+
- name: Package binary (Unix)
|
|
64
|
+
if: matrix.os != 'windows-latest'
|
|
65
|
+
run: |
|
|
66
|
+
name=pysentry-${{ matrix.name }}
|
|
67
|
+
mkdir $name
|
|
68
|
+
cp target/${{ matrix.target }}/release/${{ matrix.artifact_name }} $name/
|
|
69
|
+
cp README.md $name/
|
|
70
|
+
cp LICENSE $name/
|
|
71
|
+
tar -czf $name.tar.gz $name
|
|
72
|
+
echo "ASSET=$name.tar.gz" >> $GITHUB_ENV
|
|
73
|
+
|
|
74
|
+
- name: Package binary (Windows)
|
|
75
|
+
if: matrix.os == 'windows-latest'
|
|
76
|
+
run: |
|
|
77
|
+
$name = "pysentry-${{ matrix.name }}"
|
|
78
|
+
mkdir $name
|
|
79
|
+
cp target/${{ matrix.target }}/release/${{ matrix.artifact_name }} $name/
|
|
80
|
+
cp README.md $name/
|
|
81
|
+
cp LICENSE $name/
|
|
82
|
+
Compress-Archive -Path $name -DestinationPath "$name.zip"
|
|
83
|
+
echo "ASSET=$name.zip" | Out-File -FilePath $env:GITHUB_ENV -Append
|
|
84
|
+
|
|
85
|
+
- name: Upload binary artifact
|
|
86
|
+
uses: actions/upload-artifact@v4
|
|
87
|
+
with:
|
|
88
|
+
name: binary-${{ matrix.name }}
|
|
89
|
+
path: ${{ env.ASSET }}
|
|
90
|
+
retention-days: 7
|
|
91
|
+
|
|
92
|
+
test-build-python-wheels:
|
|
93
|
+
name: Test Build Python Wheels
|
|
94
|
+
runs-on: ${{ matrix.osarch.os }}
|
|
95
|
+
strategy:
|
|
96
|
+
fail-fast: false
|
|
97
|
+
matrix:
|
|
98
|
+
osarch:
|
|
99
|
+
- os: ubuntu-latest
|
|
100
|
+
target: x86_64-unknown-linux-gnu
|
|
101
|
+
- os: ubuntu-latest
|
|
102
|
+
target: aarch64-unknown-linux-gnu
|
|
103
|
+
- os: windows-latest
|
|
104
|
+
target: x86_64-pc-windows-msvc
|
|
105
|
+
- os: macos-latest
|
|
106
|
+
target: x86_64-apple-darwin
|
|
107
|
+
- os: macos-latest
|
|
108
|
+
target: aarch64-apple-darwin
|
|
109
|
+
python: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
110
|
+
steps:
|
|
111
|
+
- uses: actions/checkout@v4
|
|
112
|
+
|
|
113
|
+
- name: Install system dependencies (Ubuntu)
|
|
114
|
+
if: startsWith(matrix.osarch.os, 'ubuntu')
|
|
115
|
+
run: sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config
|
|
116
|
+
|
|
117
|
+
- name: Set up Python
|
|
118
|
+
uses: actions/setup-python@v5
|
|
119
|
+
with:
|
|
120
|
+
python-version: ${{ matrix.python }}
|
|
121
|
+
allow-prereleases: true
|
|
122
|
+
|
|
123
|
+
- name: Install Rust
|
|
124
|
+
uses: dtolnay/rust-toolchain@stable
|
|
125
|
+
|
|
126
|
+
- name: Set manylinux version
|
|
127
|
+
id: manylinux
|
|
128
|
+
if: startsWith(matrix.osarch.os, 'ubuntu')
|
|
129
|
+
run: |
|
|
130
|
+
if [[ "${{ matrix.osarch.target }}" == "aarch64-unknown-linux-gnu" ]]; then
|
|
131
|
+
echo "version=2_28" >> $GITHUB_OUTPUT
|
|
132
|
+
else
|
|
133
|
+
echo "version=auto" >> $GITHUB_OUTPUT
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
- name: Build wheels
|
|
137
|
+
uses: PyO3/maturin-action@v1.49.4
|
|
138
|
+
with:
|
|
139
|
+
command: build
|
|
140
|
+
args: --release --features python --out dist -i ${{ matrix.python }}
|
|
141
|
+
sccache: "true"
|
|
142
|
+
manylinux: ${{ steps.manylinux.outputs.version || 'auto' }}
|
|
143
|
+
target: ${{ matrix.osarch.target }}
|
|
144
|
+
|
|
145
|
+
- name: Upload wheels
|
|
146
|
+
uses: actions/upload-artifact@v4
|
|
147
|
+
with:
|
|
148
|
+
name: wheels-${{ matrix.osarch.os }}-${{ matrix.osarch.target }}-${{ matrix.python }}
|
|
149
|
+
path: dist/*.whl
|
|
150
|
+
retention-days: 7
|
|
151
|
+
|
|
152
|
+
test-build-sdist:
|
|
153
|
+
name: Test Build Source Distribution
|
|
154
|
+
runs-on: ubuntu-latest
|
|
155
|
+
steps:
|
|
156
|
+
- uses: actions/checkout@v4
|
|
157
|
+
|
|
158
|
+
- name: Set up Python
|
|
159
|
+
uses: actions/setup-python@v5
|
|
160
|
+
with:
|
|
161
|
+
python-version: "3.12"
|
|
162
|
+
|
|
163
|
+
- name: Install Rust
|
|
164
|
+
uses: dtolnay/rust-toolchain@stable
|
|
165
|
+
|
|
166
|
+
- name: Build sdist
|
|
167
|
+
uses: PyO3/maturin-action@v1.49.3
|
|
168
|
+
with:
|
|
169
|
+
command: sdist
|
|
170
|
+
args: --out dist
|
|
171
|
+
|
|
172
|
+
- name: Upload sdist
|
|
173
|
+
uses: actions/upload-artifact@v4
|
|
174
|
+
with:
|
|
175
|
+
name: sdist
|
|
176
|
+
path: dist/*.tar.gz
|
|
177
|
+
retention-days: 7
|
|
178
|
+
|
|
179
|
+
test-release-summary:
|
|
180
|
+
name: Release Test Summary
|
|
181
|
+
runs-on: ubuntu-latest
|
|
182
|
+
needs: [test-build-binaries, test-build-python-wheels, test-build-sdist]
|
|
183
|
+
if: always()
|
|
184
|
+
steps:
|
|
185
|
+
- name: Check build results
|
|
186
|
+
run: |
|
|
187
|
+
echo "## Build Results" >> $GITHUB_STEP_SUMMARY
|
|
188
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
189
|
+
|
|
190
|
+
if [ "${{ needs.test-build-binaries.result }}" == "success" ]; then
|
|
191
|
+
echo "✅ Binary builds: SUCCESS" >> $GITHUB_STEP_SUMMARY
|
|
192
|
+
else
|
|
193
|
+
echo "❌ Binary builds: FAILED" >> $GITHUB_STEP_SUMMARY
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
if [ "${{ needs.test-build-python-wheels.result }}" == "success" ]; then
|
|
197
|
+
echo "✅ Python wheel builds: SUCCESS" >> $GITHUB_STEP_SUMMARY
|
|
198
|
+
else
|
|
199
|
+
echo "❌ Python wheel builds: FAILED" >> $GITHUB_STEP_SUMMARY
|
|
200
|
+
fi
|
|
201
|
+
|
|
202
|
+
if [ "${{ needs.test-build-sdist.result }}" == "success" ]; then
|
|
203
|
+
echo "✅ Source distribution build: SUCCESS" >> $GITHUB_STEP_SUMMARY
|
|
204
|
+
else
|
|
205
|
+
echo "❌ Source distribution build: FAILED" >> $GITHUB_STEP_SUMMARY
|
|
206
|
+
fi
|
|
207
|
+
|
|
208
|
+
echo "" >> $GITHUB_STEP_SUMMARY
|
|
209
|
+
echo "All artifacts are available in the workflow run for 7 days." >> $GITHUB_STEP_SUMMARY
|
|
210
|
+
|
|
211
|
+
- name: Fail if any build failed
|
|
212
|
+
if: needs.test-build-binaries.result != 'success' || needs.test-build-python-wheels.result != 'success' || needs.test-build-sdist.result != 'success'
|
|
213
|
+
run: exit 1
|
|
@@ -136,80 +136,26 @@ jobs:
|
|
|
136
136
|
|
|
137
137
|
build-python-wheels:
|
|
138
138
|
name: Build Python Wheels
|
|
139
|
-
runs-on: ${{ matrix.os }}
|
|
139
|
+
runs-on: ${{ matrix.osarch.os }}
|
|
140
140
|
strategy:
|
|
141
|
-
fail-fast: false
|
|
142
141
|
matrix:
|
|
143
|
-
|
|
144
|
-
# x86_64 Linux
|
|
142
|
+
osarch:
|
|
145
143
|
- os: ubuntu-latest
|
|
146
144
|
target: x86_64-unknown-linux-gnu
|
|
147
|
-
python: "3.9"
|
|
148
145
|
- os: ubuntu-latest
|
|
149
|
-
target:
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
target: x86_64-unknown-linux-gnu
|
|
153
|
-
python: "3.11"
|
|
154
|
-
- os: ubuntu-latest
|
|
155
|
-
target: x86_64-unknown-linux-gnu
|
|
156
|
-
python: "3.12"
|
|
157
|
-
- os: ubuntu-latest
|
|
158
|
-
target: x86_64-unknown-linux-gnu
|
|
159
|
-
python: "3.13"
|
|
160
|
-
# macOS
|
|
161
|
-
- os: macos-latest
|
|
162
|
-
target: x86_64-apple-darwin
|
|
163
|
-
python: "3.9"
|
|
164
|
-
- os: macos-latest
|
|
165
|
-
target: x86_64-apple-darwin
|
|
166
|
-
python: "3.10"
|
|
167
|
-
- os: macos-latest
|
|
168
|
-
target: x86_64-apple-darwin
|
|
169
|
-
python: "3.11"
|
|
170
|
-
- os: macos-latest
|
|
171
|
-
target: x86_64-apple-darwin
|
|
172
|
-
python: "3.12"
|
|
146
|
+
target: aarch64-unknown-linux-gnu
|
|
147
|
+
- os: windows-latest
|
|
148
|
+
target: x86_64-pc-windows-msvc
|
|
173
149
|
- os: macos-latest
|
|
174
150
|
target: x86_64-apple-darwin
|
|
175
|
-
python: "3.13"
|
|
176
151
|
- os: macos-latest
|
|
177
152
|
target: aarch64-apple-darwin
|
|
178
|
-
|
|
179
|
-
- os: macos-latest
|
|
180
|
-
target: aarch64-apple-darwin
|
|
181
|
-
python: "3.10"
|
|
182
|
-
- os: macos-latest
|
|
183
|
-
target: aarch64-apple-darwin
|
|
184
|
-
python: "3.11"
|
|
185
|
-
- os: macos-latest
|
|
186
|
-
target: aarch64-apple-darwin
|
|
187
|
-
python: "3.12"
|
|
188
|
-
- os: macos-latest
|
|
189
|
-
target: aarch64-apple-darwin
|
|
190
|
-
python: "3.13"
|
|
191
|
-
# Windows
|
|
192
|
-
- os: windows-latest
|
|
193
|
-
target: x86_64-pc-windows-msvc
|
|
194
|
-
python: "3.9"
|
|
195
|
-
- os: windows-latest
|
|
196
|
-
target: x86_64-pc-windows-msvc
|
|
197
|
-
python: "3.10"
|
|
198
|
-
- os: windows-latest
|
|
199
|
-
target: x86_64-pc-windows-msvc
|
|
200
|
-
python: "3.11"
|
|
201
|
-
- os: windows-latest
|
|
202
|
-
target: x86_64-pc-windows-msvc
|
|
203
|
-
python: "3.12"
|
|
204
|
-
- os: windows-latest
|
|
205
|
-
target: x86_64-pc-windows-msvc
|
|
206
|
-
python: "3.13"
|
|
207
|
-
|
|
153
|
+
python: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
208
154
|
steps:
|
|
209
155
|
- uses: actions/checkout@v4
|
|
210
156
|
|
|
211
157
|
- name: Install system dependencies (Ubuntu)
|
|
212
|
-
if: startsWith(matrix.os, 'ubuntu')
|
|
158
|
+
if: startsWith(matrix.osarch.os, 'ubuntu')
|
|
213
159
|
run: sudo apt-get update && sudo apt-get install -y libssl-dev pkg-config
|
|
214
160
|
|
|
215
161
|
- name: Set up Python
|
|
@@ -220,19 +166,29 @@ jobs:
|
|
|
220
166
|
- name: Install Rust
|
|
221
167
|
uses: dtolnay/rust-toolchain@stable
|
|
222
168
|
|
|
169
|
+
- name: Set manylinux version
|
|
170
|
+
id: manylinux
|
|
171
|
+
if: startsWith(matrix.osarch.os, 'ubuntu')
|
|
172
|
+
run: |
|
|
173
|
+
if [[ "${{ matrix.osarch.target }}" == "aarch64-unknown-linux-gnu" ]]; then
|
|
174
|
+
echo "version=2_28" >> $GITHUB_OUTPUT
|
|
175
|
+
else
|
|
176
|
+
echo "version=auto" >> $GITHUB_OUTPUT
|
|
177
|
+
fi
|
|
178
|
+
|
|
223
179
|
- name: Build wheels
|
|
224
180
|
uses: PyO3/maturin-action@v1.49.4
|
|
225
181
|
with:
|
|
226
182
|
command: build
|
|
227
183
|
args: --release --features python --out dist -i ${{ matrix.python }}
|
|
228
184
|
sccache: "true"
|
|
229
|
-
manylinux: auto
|
|
230
|
-
target: ${{ matrix.target }}
|
|
185
|
+
manylinux: ${{ steps.manylinux.outputs.version || 'auto' }}
|
|
186
|
+
target: ${{ matrix.osarch.target }}
|
|
231
187
|
|
|
232
188
|
- name: Upload wheels
|
|
233
189
|
uses: actions/upload-artifact@v4
|
|
234
190
|
with:
|
|
235
|
-
name: wheels-${{ matrix.os }}-${{ matrix.target }}-${{ matrix.python }}
|
|
191
|
+
name: wheels-${{ matrix.osarch.os }}-${{ matrix.osarch.target }}-${{ matrix.python }}
|
|
236
192
|
path: dist/*.whl
|
|
237
193
|
|
|
238
194
|
build-sdist:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pysentry-rs
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.12
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
@@ -78,7 +78,7 @@ This method:
|
|
|
78
78
|
|
|
79
79
|
### 📦 From PyPI (Python Package)
|
|
80
80
|
|
|
81
|
-
For Python 3.9
|
|
81
|
+
For Python 3.9-3.14 on Linux, macOS, and Windows:
|
|
82
82
|
|
|
83
83
|
```bash
|
|
84
84
|
pip install pysentry-rs
|
|
@@ -129,20 +129,23 @@ The binary will be available at `target/release/pysentry`.
|
|
|
129
129
|
|
|
130
130
|
### Requirements
|
|
131
131
|
|
|
132
|
-
- **For uvx**: Python 3.9
|
|
132
|
+
- **For uvx**: Python 3.9-3.14 and [uv](https://docs.astral.sh/uv/) installed
|
|
133
133
|
- **For binaries**: No additional dependencies
|
|
134
|
-
- **For Python package**: Python 3.9
|
|
134
|
+
- **For Python package**: Python 3.9-3.14
|
|
135
135
|
- **For Rust package and source**: Rust 1.79+
|
|
136
136
|
|
|
137
137
|
### Platform Support
|
|
138
138
|
|
|
139
|
-
| Installation Method | Linux | macOS | Windows |
|
|
140
|
-
| ------------------- |
|
|
141
|
-
| uvx | ✅
|
|
142
|
-
| PyPI (pip) | ✅
|
|
143
|
-
| Crates.io (cargo) | ✅
|
|
144
|
-
| GitHub Releases | ✅
|
|
145
|
-
| From Source | ✅
|
|
139
|
+
| Installation Method | Linux (x64) | Linux (ARM64) | macOS (x64) | macOS (ARM64) | Windows (x64) |
|
|
140
|
+
| ------------------- | ----------- | ------------- | ----------- | ------------- | ------------- |
|
|
141
|
+
| uvx | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
142
|
+
| PyPI (pip) | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
143
|
+
| Crates.io (cargo) | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
144
|
+
| GitHub Releases | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
145
|
+
| From Source | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
146
|
+
|
|
147
|
+
**Supported Python Versions**: 3.9, 3.10, 3.11, 3.12, 3.13, 3.14
|
|
148
|
+
**Supported Architectures**: x86_64 (x64), ARM64 (aarch64)
|
|
146
149
|
|
|
147
150
|
|
|
148
151
|
### CLI Command Names
|
|
@@ -302,7 +305,7 @@ Add PySentry to your `.pre-commit-config.yaml`:
|
|
|
302
305
|
```yaml
|
|
303
306
|
repos:
|
|
304
307
|
- repo: https://github.com/pysentry/pysentry-pre-commit
|
|
305
|
-
rev: v0.3.
|
|
308
|
+
rev: v0.3.11
|
|
306
309
|
hooks:
|
|
307
310
|
- id: pysentry # default pysentry settings
|
|
308
311
|
```
|
|
@@ -312,7 +315,7 @@ repos:
|
|
|
312
315
|
```yaml
|
|
313
316
|
repos:
|
|
314
317
|
- repo: https://github.com/pysentry/pysentry-pre-commit
|
|
315
|
-
rev: v0.3.
|
|
318
|
+
rev: v0.3.11
|
|
316
319
|
hooks:
|
|
317
320
|
- id: pysentry
|
|
318
321
|
args: ["--sources", "pypa,osv", "--fail-on", "high"]
|
|
@@ -53,7 +53,7 @@ This method:
|
|
|
53
53
|
|
|
54
54
|
### 📦 From PyPI (Python Package)
|
|
55
55
|
|
|
56
|
-
For Python 3.9
|
|
56
|
+
For Python 3.9-3.14 on Linux, macOS, and Windows:
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
59
|
pip install pysentry-rs
|
|
@@ -104,20 +104,23 @@ The binary will be available at `target/release/pysentry`.
|
|
|
104
104
|
|
|
105
105
|
### Requirements
|
|
106
106
|
|
|
107
|
-
- **For uvx**: Python 3.9
|
|
107
|
+
- **For uvx**: Python 3.9-3.14 and [uv](https://docs.astral.sh/uv/) installed
|
|
108
108
|
- **For binaries**: No additional dependencies
|
|
109
|
-
- **For Python package**: Python 3.9
|
|
109
|
+
- **For Python package**: Python 3.9-3.14
|
|
110
110
|
- **For Rust package and source**: Rust 1.79+
|
|
111
111
|
|
|
112
112
|
### Platform Support
|
|
113
113
|
|
|
114
|
-
| Installation Method | Linux | macOS | Windows |
|
|
115
|
-
| ------------------- |
|
|
116
|
-
| uvx | ✅
|
|
117
|
-
| PyPI (pip) | ✅
|
|
118
|
-
| Crates.io (cargo) | ✅
|
|
119
|
-
| GitHub Releases | ✅
|
|
120
|
-
| From Source | ✅
|
|
114
|
+
| Installation Method | Linux (x64) | Linux (ARM64) | macOS (x64) | macOS (ARM64) | Windows (x64) |
|
|
115
|
+
| ------------------- | ----------- | ------------- | ----------- | ------------- | ------------- |
|
|
116
|
+
| uvx | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
117
|
+
| PyPI (pip) | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
118
|
+
| Crates.io (cargo) | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
119
|
+
| GitHub Releases | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
120
|
+
| From Source | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
121
|
+
|
|
122
|
+
**Supported Python Versions**: 3.9, 3.10, 3.11, 3.12, 3.13, 3.14
|
|
123
|
+
**Supported Architectures**: x86_64 (x64), ARM64 (aarch64)
|
|
121
124
|
|
|
122
125
|
|
|
123
126
|
### CLI Command Names
|
|
@@ -277,7 +280,7 @@ Add PySentry to your `.pre-commit-config.yaml`:
|
|
|
277
280
|
```yaml
|
|
278
281
|
repos:
|
|
279
282
|
- repo: https://github.com/pysentry/pysentry-pre-commit
|
|
280
|
-
rev: v0.3.
|
|
283
|
+
rev: v0.3.11
|
|
281
284
|
hooks:
|
|
282
285
|
- id: pysentry # default pysentry settings
|
|
283
286
|
```
|
|
@@ -287,7 +290,7 @@ repos:
|
|
|
287
290
|
```yaml
|
|
288
291
|
repos:
|
|
289
292
|
- repo: https://github.com/pysentry/pysentry-pre-commit
|
|
290
|
-
rev: v0.3.
|
|
293
|
+
rev: v0.3.11
|
|
291
294
|
hooks:
|
|
292
295
|
- id: pysentry
|
|
293
296
|
args: ["--sources", "pypa,osv", "--fail-on", "high"]
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# PySentry - pip-audit Benchmark Report
|
|
2
|
+
|
|
3
|
+
**Generated:** 2025-10-01 17:03:50
|
|
4
|
+
**Duration:** 1m 58.52s
|
|
5
|
+
**Total Tests:** 20
|
|
6
|
+
|
|
7
|
+
## Executive Summary
|
|
8
|
+
|
|
9
|
+
**Overall Success Rate:** 100.0% (20/20 successful runs)
|
|
10
|
+
|
|
11
|
+
### Small_Requirements Dataset - Cold Cache
|
|
12
|
+
- **Fastest:** pysentry-pypi (0.251s) - 35.82x faster than slowest
|
|
13
|
+
- **Memory Efficient:** pysentry-osv (9.73 MB) - 8.91x less memory than highest
|
|
14
|
+
|
|
15
|
+
### Small_Requirements Dataset - Hot Cache
|
|
16
|
+
- **Fastest:** pysentry-pypi (0.225s) - 35.73x faster than slowest
|
|
17
|
+
- **Memory Efficient:** pysentry-osv (9.61 MB) - 9.75x less memory than highest
|
|
18
|
+
|
|
19
|
+
### Large_Requirements Dataset - Cold Cache
|
|
20
|
+
- **Fastest:** pysentry-pypi (0.725s) - 27.37x faster than slowest
|
|
21
|
+
- **Memory Efficient:** pysentry-osv (10.55 MB) - 8.94x less memory than highest
|
|
22
|
+
|
|
23
|
+
### Large_Requirements Dataset - Hot Cache
|
|
24
|
+
- **Fastest:** pysentry-pypi (0.710s) - 22.73x faster than slowest
|
|
25
|
+
- **Memory Efficient:** pysentry-osv (9.75 MB) - 8.22x less memory than highest
|
|
26
|
+
|
|
27
|
+
## Test Environment
|
|
28
|
+
|
|
29
|
+
- **Platform:** Linux-6.11.0-1018-azure-x86_64-with-glibc2.39
|
|
30
|
+
- **Python Version:** 3.11.13
|
|
31
|
+
- **CPU Cores:** 4
|
|
32
|
+
- **Total Memory:** 15.62 GB
|
|
33
|
+
- **Available Memory:** 14.68 GB
|
|
34
|
+
|
|
35
|
+
## Performance Comparison
|
|
36
|
+
|
|
37
|
+
### Small_Requirements Dataset - Cold Cache
|
|
38
|
+
|
|
39
|
+
#### Execution Time Comparison
|
|
40
|
+
|
|
41
|
+
| Tool Configuration | Execution Time | Relative Performance |
|
|
42
|
+
|---------------------|---------------------|---------------------|
|
|
43
|
+
| 🥇 pysentry-pypi | 0.251s | 1.00x |
|
|
44
|
+
| 🥈 pysentry-osv | 0.800s | 3.18x |
|
|
45
|
+
| pysentry-all-sources | 1.105s | 4.40x |
|
|
46
|
+
| pysentry-pypa | 1.344s | 5.35x |
|
|
47
|
+
| pip-audit-default | 8.995s | 35.82x |
|
|
48
|
+
|
|
49
|
+
#### Memory Usage Comparison
|
|
50
|
+
|
|
51
|
+
| Tool Configuration | Peak Memory | Relative Performance |
|
|
52
|
+
|---------------------|---------------------|---------------------|
|
|
53
|
+
| 🥇 pysentry-osv | 9.73 MB | 1.00x |
|
|
54
|
+
| 🥈 pysentry-pypi | 10.30 MB | 1.06x |
|
|
55
|
+
| pip-audit-default | 45.71 MB | 4.70x |
|
|
56
|
+
| pysentry-pypa | 60.87 MB | 6.26x |
|
|
57
|
+
| pysentry-all-sources | 86.62 MB | 8.91x |
|
|
58
|
+
|
|
59
|
+
### Small_Requirements Dataset - Hot Cache
|
|
60
|
+
|
|
61
|
+
#### Execution Time Comparison
|
|
62
|
+
|
|
63
|
+
| Tool Configuration | Execution Time | Relative Performance |
|
|
64
|
+
|---------------------|---------------------|---------------------|
|
|
65
|
+
| 🥇 pysentry-pypi | 0.225s | 1.00x |
|
|
66
|
+
| 🥈 pysentry-pypa | 0.693s | 3.08x |
|
|
67
|
+
| pysentry-osv | 0.781s | 3.47x |
|
|
68
|
+
| pysentry-all-sources | 0.819s | 3.64x |
|
|
69
|
+
| pip-audit-default | 8.033s | 35.73x |
|
|
70
|
+
|
|
71
|
+
#### Memory Usage Comparison
|
|
72
|
+
|
|
73
|
+
| Tool Configuration | Peak Memory | Relative Performance |
|
|
74
|
+
|---------------------|---------------------|---------------------|
|
|
75
|
+
| 🥇 pysentry-osv | 9.61 MB | 1.00x |
|
|
76
|
+
| 🥈 pysentry-pypi | 10.67 MB | 1.11x |
|
|
77
|
+
| pysentry-pypa | 43.20 MB | 4.49x |
|
|
78
|
+
| pip-audit-default | 44.82 MB | 4.66x |
|
|
79
|
+
| pysentry-all-sources | 93.77 MB | 9.75x |
|
|
80
|
+
|
|
81
|
+
### Large_Requirements Dataset - Cold Cache
|
|
82
|
+
|
|
83
|
+
#### Execution Time Comparison
|
|
84
|
+
|
|
85
|
+
| Tool Configuration | Execution Time | Relative Performance |
|
|
86
|
+
|---------------------|---------------------|---------------------|
|
|
87
|
+
| 🥇 pysentry-pypi | 0.725s | 1.00x |
|
|
88
|
+
| 🥈 pysentry-pypa | 1.522s | 2.10x |
|
|
89
|
+
| pysentry-osv | 3.638s | 5.02x |
|
|
90
|
+
| pysentry-all-sources | 4.179s | 5.76x |
|
|
91
|
+
| pip-audit-default | 19.837s | 27.37x |
|
|
92
|
+
|
|
93
|
+
#### Memory Usage Comparison
|
|
94
|
+
|
|
95
|
+
| Tool Configuration | Peak Memory | Relative Performance |
|
|
96
|
+
|---------------------|---------------------|---------------------|
|
|
97
|
+
| 🥇 pysentry-osv | 10.55 MB | 1.00x |
|
|
98
|
+
| 🥈 pysentry-pypi | 12.95 MB | 1.23x |
|
|
99
|
+
| pip-audit-default | 53.12 MB | 5.03x |
|
|
100
|
+
| pysentry-pypa | 61.15 MB | 5.80x |
|
|
101
|
+
| pysentry-all-sources | 94.30 MB | 8.94x |
|
|
102
|
+
|
|
103
|
+
### Large_Requirements Dataset - Hot Cache
|
|
104
|
+
|
|
105
|
+
#### Execution Time Comparison
|
|
106
|
+
|
|
107
|
+
| Tool Configuration | Execution Time | Relative Performance |
|
|
108
|
+
|---------------------|---------------------|---------------------|
|
|
109
|
+
| 🥇 pysentry-pypi | 0.710s | 1.00x |
|
|
110
|
+
| 🥈 pysentry-pypa | 1.130s | 1.59x |
|
|
111
|
+
| pysentry-osv | 3.954s | 5.57x |
|
|
112
|
+
| pysentry-all-sources | 4.199s | 5.91x |
|
|
113
|
+
| pip-audit-default | 16.134s | 22.73x |
|
|
114
|
+
|
|
115
|
+
#### Memory Usage Comparison
|
|
116
|
+
|
|
117
|
+
| Tool Configuration | Peak Memory | Relative Performance |
|
|
118
|
+
|---------------------|---------------------|---------------------|
|
|
119
|
+
| 🥇 pysentry-osv | 9.75 MB | 1.00x |
|
|
120
|
+
| 🥈 pysentry-pypi | 12.74 MB | 1.31x |
|
|
121
|
+
| pip-audit-default | 47.41 MB | 4.86x |
|
|
122
|
+
| pysentry-pypa | 60.06 MB | 6.16x |
|
|
123
|
+
| pysentry-all-sources | 80.21 MB | 8.22x |
|
|
124
|
+
|
|
125
|
+
## Detailed Analysis
|
|
126
|
+
|
|
127
|
+
### Pysentry Performance
|
|
128
|
+
|
|
129
|
+
- **Execution Time:** Avg: 1.630s, Min: 0.225s, Max: 4.199s
|
|
130
|
+
|
|
131
|
+
- **Memory Usage:** Avg: 41.65 MB, Min: 9.61 MB, Max: 94.30 MB
|
|
132
|
+
|
|
133
|
+
- **Success Rate:** 100.0% (16/16)
|
|
134
|
+
|
|
135
|
+
### Pip-Audit Performance
|
|
136
|
+
|
|
137
|
+
- **Execution Time:** Avg: 13.250s, Min: 8.033s, Max: 19.837s
|
|
138
|
+
|
|
139
|
+
- **Memory Usage:** Avg: 47.77 MB, Min: 44.82 MB, Max: 53.12 MB
|
|
140
|
+
|
|
141
|
+
- **Success Rate:** 100.0% (4/4)
|
|
@@ -662,3 +662,89 @@ fn default_http_retry_max_backoff() -> u64 {
|
|
|
662
662
|
fn default_http_show_progress() -> bool {
|
|
663
663
|
true
|
|
664
664
|
}
|
|
665
|
+
|
|
666
|
+
#[cfg(test)]
|
|
667
|
+
mod tests {
|
|
668
|
+
use super::*;
|
|
669
|
+
use std::fs;
|
|
670
|
+
use tempfile::TempDir;
|
|
671
|
+
|
|
672
|
+
#[test]
|
|
673
|
+
fn test_config_load_from_file() {
|
|
674
|
+
let temp_dir = TempDir::new().unwrap();
|
|
675
|
+
let config_path = temp_dir.path().join(".pysentry.toml");
|
|
676
|
+
|
|
677
|
+
let config_content = r#"
|
|
678
|
+
version = 1
|
|
679
|
+
|
|
680
|
+
[defaults]
|
|
681
|
+
format = "markdown"
|
|
682
|
+
severity = "medium"
|
|
683
|
+
fail_on = "low"
|
|
684
|
+
|
|
685
|
+
[sources]
|
|
686
|
+
enabled = ["pypa", "pypi", "osv"]
|
|
687
|
+
|
|
688
|
+
[cache]
|
|
689
|
+
enabled = false
|
|
690
|
+
"#;
|
|
691
|
+
|
|
692
|
+
fs::write(&config_path, config_content).unwrap();
|
|
693
|
+
|
|
694
|
+
let loader = ConfigLoader::load_from_file(&config_path).unwrap();
|
|
695
|
+
|
|
696
|
+
assert_eq!(loader.config.defaults.format, "markdown");
|
|
697
|
+
assert_eq!(loader.config.defaults.severity, "medium");
|
|
698
|
+
assert_eq!(loader.config.defaults.fail_on, "low");
|
|
699
|
+
assert_eq!(loader.config.sources.enabled, vec!["pypa", "pypi", "osv"]);
|
|
700
|
+
assert!(!loader.config.cache.enabled);
|
|
701
|
+
assert!(loader.config_path.is_some());
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
#[test]
|
|
705
|
+
fn test_config_default_values() {
|
|
706
|
+
let config = Config::default();
|
|
707
|
+
|
|
708
|
+
assert_eq!(config.version, 1);
|
|
709
|
+
assert_eq!(config.defaults.format, "human");
|
|
710
|
+
assert_eq!(config.defaults.severity, "low");
|
|
711
|
+
assert_eq!(config.defaults.fail_on, "medium");
|
|
712
|
+
assert_eq!(config.sources.enabled, vec!["pypa"]);
|
|
713
|
+
assert!(config.cache.enabled);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
#[test]
|
|
717
|
+
fn test_config_validation() {
|
|
718
|
+
let mut config = Config::default();
|
|
719
|
+
|
|
720
|
+
// Valid config should pass
|
|
721
|
+
assert!(config.validate().is_ok());
|
|
722
|
+
|
|
723
|
+
// Invalid format
|
|
724
|
+
config.defaults.format = "invalid".to_string();
|
|
725
|
+
assert!(config.validate().is_err());
|
|
726
|
+
config.defaults.format = "human".to_string();
|
|
727
|
+
|
|
728
|
+
// Invalid severity
|
|
729
|
+
config.defaults.severity = "invalid".to_string();
|
|
730
|
+
assert!(config.validate().is_err());
|
|
731
|
+
config.defaults.severity = "low".to_string();
|
|
732
|
+
|
|
733
|
+
// Invalid source
|
|
734
|
+
config.sources.enabled = vec!["invalid".to_string()];
|
|
735
|
+
assert!(config.validate().is_err());
|
|
736
|
+
config.sources.enabled = vec!["pypa".to_string()];
|
|
737
|
+
|
|
738
|
+
// Empty sources
|
|
739
|
+
config.sources.enabled = vec![];
|
|
740
|
+
assert!(config.validate().is_err());
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
#[test]
|
|
744
|
+
fn test_config_loader_with_no_config() {
|
|
745
|
+
// Test that loader works even when no config file exists
|
|
746
|
+
let loader = ConfigLoader::load_with_options(true).unwrap();
|
|
747
|
+
assert!(loader.config_path.is_none());
|
|
748
|
+
assert_eq!(loader.config.defaults.format, "human");
|
|
749
|
+
}
|
|
750
|
+
}
|
|
@@ -314,6 +314,80 @@ impl RequirementsParser {
|
|
|
314
314
|
Ok(filtered_dependencies)
|
|
315
315
|
}
|
|
316
316
|
|
|
317
|
+
/// Parse include directive (-r or -c) from a requirements line
|
|
318
|
+
fn parse_include_directive(line: &str) -> Option<(&str, &str)> {
|
|
319
|
+
let trimmed = line.trim();
|
|
320
|
+
|
|
321
|
+
// Handle -r and --requirement (for requirements includes)
|
|
322
|
+
if let Some(path) = trimmed.strip_prefix("-r ") {
|
|
323
|
+
return Some(("r", path.trim()));
|
|
324
|
+
}
|
|
325
|
+
if let Some(path) = trimmed.strip_prefix("--requirement ") {
|
|
326
|
+
return Some(("r", path.trim()));
|
|
327
|
+
}
|
|
328
|
+
if let Some(path) = trimmed.strip_prefix("--requirement=") {
|
|
329
|
+
return Some(("r", path.trim()));
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Handle -c and --constraint (for constraint files - same issue)
|
|
333
|
+
if let Some(path) = trimmed.strip_prefix("-c ") {
|
|
334
|
+
return Some(("c", path.trim()));
|
|
335
|
+
}
|
|
336
|
+
if let Some(path) = trimmed.strip_prefix("--constraint ") {
|
|
337
|
+
return Some(("c", path.trim()));
|
|
338
|
+
}
|
|
339
|
+
if let Some(path) = trimmed.strip_prefix("--constraint=") {
|
|
340
|
+
return Some(("c", path.trim()));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
None
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/// Resolve relative -r/-c directive paths to absolute paths
|
|
347
|
+
fn resolve_requirements_includes(content: &str, source_file: &Path) -> Result<String> {
|
|
348
|
+
let parent_dir = source_file.parent().ok_or_else(|| {
|
|
349
|
+
AuditError::other(format!(
|
|
350
|
+
"Cannot get parent directory for: {}",
|
|
351
|
+
source_file.display()
|
|
352
|
+
))
|
|
353
|
+
})?;
|
|
354
|
+
|
|
355
|
+
let mut resolved = String::new();
|
|
356
|
+
|
|
357
|
+
for line in content.lines() {
|
|
358
|
+
if let Some((directive_type, rel_path)) = Self::parse_include_directive(line) {
|
|
359
|
+
// Resolve relative path to absolute path
|
|
360
|
+
let include_path = parent_dir.join(rel_path);
|
|
361
|
+
|
|
362
|
+
match include_path.canonicalize() {
|
|
363
|
+
Ok(abs_path) => {
|
|
364
|
+
let flag = if directive_type == "r" { "-r" } else { "-c" };
|
|
365
|
+
debug!("Resolved {} {} -> {}", flag, rel_path, abs_path.display());
|
|
366
|
+
resolved.push_str(&format!("{} {}\n", flag, abs_path.display()));
|
|
367
|
+
}
|
|
368
|
+
Err(e) => {
|
|
369
|
+
// If file doesn't exist, keep original path and let UV handle the error
|
|
370
|
+
// This preserves existing error messages from UV
|
|
371
|
+
warn!(
|
|
372
|
+
"Could not resolve include path '{}' from {}: {}. Using original path.",
|
|
373
|
+
rel_path,
|
|
374
|
+
source_file.display(),
|
|
375
|
+
e
|
|
376
|
+
);
|
|
377
|
+
resolved.push_str(line);
|
|
378
|
+
resolved.push('\n');
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
} else {
|
|
382
|
+
// Not an include directive, keep line as-is
|
|
383
|
+
resolved.push_str(line);
|
|
384
|
+
resolved.push('\n');
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
Ok(resolved)
|
|
389
|
+
}
|
|
390
|
+
|
|
317
391
|
/// Combine multiple explicit requirements files into single content
|
|
318
392
|
async fn combine_explicit_files(&self, files: &[PathBuf]) -> Result<String> {
|
|
319
393
|
let mut combined = String::new();
|
|
@@ -329,8 +403,11 @@ impl RequirementsParser {
|
|
|
329
403
|
))
|
|
330
404
|
})?;
|
|
331
405
|
|
|
406
|
+
// Resolve relative -r/-c directives to absolute paths
|
|
407
|
+
let resolved_content = Self::resolve_requirements_includes(&content, file)?;
|
|
408
|
+
|
|
332
409
|
combined.push_str(&format!("# From: {}\n", file.display()));
|
|
333
|
-
combined.push_str(&
|
|
410
|
+
combined.push_str(&resolved_content);
|
|
334
411
|
combined.push('\n');
|
|
335
412
|
}
|
|
336
413
|
|
|
@@ -506,4 +583,184 @@ click>=8.0.0
|
|
|
506
583
|
assert!(result.contains(&PackageName::new("flask")));
|
|
507
584
|
assert!(result.contains(&PackageName::new("click")));
|
|
508
585
|
}
|
|
586
|
+
|
|
587
|
+
#[test]
|
|
588
|
+
fn test_parse_include_directive() {
|
|
589
|
+
// Test -r directive
|
|
590
|
+
assert_eq!(
|
|
591
|
+
RequirementsParser::parse_include_directive("-r ../requirements.txt"),
|
|
592
|
+
Some(("r", "../requirements.txt"))
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
// Test --requirement directive
|
|
596
|
+
assert_eq!(
|
|
597
|
+
RequirementsParser::parse_include_directive("--requirement base.txt"),
|
|
598
|
+
Some(("r", "base.txt"))
|
|
599
|
+
);
|
|
600
|
+
|
|
601
|
+
// Test --requirement= directive
|
|
602
|
+
assert_eq!(
|
|
603
|
+
RequirementsParser::parse_include_directive("--requirement=../requirements.txt"),
|
|
604
|
+
Some(("r", "../requirements.txt"))
|
|
605
|
+
);
|
|
606
|
+
|
|
607
|
+
// Test -c constraint directive
|
|
608
|
+
assert_eq!(
|
|
609
|
+
RequirementsParser::parse_include_directive("-c constraints.txt"),
|
|
610
|
+
Some(("c", "constraints.txt"))
|
|
611
|
+
);
|
|
612
|
+
|
|
613
|
+
// Test --constraint directive
|
|
614
|
+
assert_eq!(
|
|
615
|
+
RequirementsParser::parse_include_directive("--constraint ../constraints.txt"),
|
|
616
|
+
Some(("c", "../constraints.txt"))
|
|
617
|
+
);
|
|
618
|
+
|
|
619
|
+
// Test --constraint= directive
|
|
620
|
+
assert_eq!(
|
|
621
|
+
RequirementsParser::parse_include_directive("--constraint=constraints.txt"),
|
|
622
|
+
Some(("c", "constraints.txt"))
|
|
623
|
+
);
|
|
624
|
+
|
|
625
|
+
// Test with extra whitespace
|
|
626
|
+
assert_eq!(
|
|
627
|
+
RequirementsParser::parse_include_directive(" -r ../requirements.txt "),
|
|
628
|
+
Some(("r", "../requirements.txt"))
|
|
629
|
+
);
|
|
630
|
+
|
|
631
|
+
// Test non-directive lines
|
|
632
|
+
assert_eq!(
|
|
633
|
+
RequirementsParser::parse_include_directive("requests>=2.25.0"),
|
|
634
|
+
None
|
|
635
|
+
);
|
|
636
|
+
assert_eq!(
|
|
637
|
+
RequirementsParser::parse_include_directive("# Comment"),
|
|
638
|
+
None
|
|
639
|
+
);
|
|
640
|
+
assert_eq!(RequirementsParser::parse_include_directive(""), None);
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
#[test]
|
|
644
|
+
fn test_resolve_requirements_includes() {
|
|
645
|
+
use std::fs;
|
|
646
|
+
use tempfile::TempDir;
|
|
647
|
+
|
|
648
|
+
// Create temporary directory structure
|
|
649
|
+
let temp_dir = TempDir::new().unwrap();
|
|
650
|
+
let project_path = temp_dir.path();
|
|
651
|
+
|
|
652
|
+
// Create requirements files
|
|
653
|
+
let base_req = project_path.join("requirements.txt");
|
|
654
|
+
fs::write(&base_req, "flask==2.0.0\n").unwrap();
|
|
655
|
+
|
|
656
|
+
// Create dev subdirectory
|
|
657
|
+
let dev_dir = project_path.join("dev");
|
|
658
|
+
fs::create_dir(&dev_dir).unwrap();
|
|
659
|
+
let dev_req = dev_dir.join("requirements.txt");
|
|
660
|
+
|
|
661
|
+
// Test content with relative path
|
|
662
|
+
let content_with_include = "-r ../requirements.txt\npytest==8.4.2\n";
|
|
663
|
+
|
|
664
|
+
let resolved =
|
|
665
|
+
RequirementsParser::resolve_requirements_includes(content_with_include, &dev_req)
|
|
666
|
+
.unwrap();
|
|
667
|
+
|
|
668
|
+
// Should contain absolute path to requirements.txt
|
|
669
|
+
assert!(resolved.contains("-r"));
|
|
670
|
+
assert!(resolved.contains("requirements.txt"));
|
|
671
|
+
assert!(resolved.contains("pytest==8.4.2"));
|
|
672
|
+
|
|
673
|
+
// Verify the path was made absolute
|
|
674
|
+
let expected_abs_path = base_req.canonicalize().unwrap();
|
|
675
|
+
assert!(resolved.contains(&expected_abs_path.display().to_string()));
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
#[test]
|
|
679
|
+
fn test_resolve_requirements_includes_constraint_files() {
|
|
680
|
+
use std::fs;
|
|
681
|
+
use tempfile::TempDir;
|
|
682
|
+
|
|
683
|
+
let temp_dir = TempDir::new().unwrap();
|
|
684
|
+
let project_path = temp_dir.path();
|
|
685
|
+
|
|
686
|
+
// Create constraint file
|
|
687
|
+
let constraints = project_path.join("constraints.txt");
|
|
688
|
+
fs::write(&constraints, "flask<3.0\n").unwrap();
|
|
689
|
+
|
|
690
|
+
// Create dev subdirectory
|
|
691
|
+
let dev_dir = project_path.join("dev");
|
|
692
|
+
fs::create_dir(&dev_dir).unwrap();
|
|
693
|
+
let dev_req = dev_dir.join("requirements.txt");
|
|
694
|
+
|
|
695
|
+
let content = "-c ../constraints.txt\nflask>=2.0\n";
|
|
696
|
+
|
|
697
|
+
let resolved =
|
|
698
|
+
RequirementsParser::resolve_requirements_includes(content, &dev_req).unwrap();
|
|
699
|
+
|
|
700
|
+
// Should contain absolute path to constraints.txt
|
|
701
|
+
assert!(resolved.contains("-c"));
|
|
702
|
+
assert!(resolved.contains("constraints.txt"));
|
|
703
|
+
assert!(resolved.contains("flask>=2.0"));
|
|
704
|
+
|
|
705
|
+
let expected_abs_path = constraints.canonicalize().unwrap();
|
|
706
|
+
assert!(resolved.contains(&expected_abs_path.display().to_string()));
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
#[test]
|
|
710
|
+
fn test_resolve_requirements_includes_preserves_regular_lines() {
|
|
711
|
+
use std::fs;
|
|
712
|
+
use tempfile::TempDir;
|
|
713
|
+
|
|
714
|
+
let temp_dir = TempDir::new().unwrap();
|
|
715
|
+
let test_file = temp_dir.path().join("requirements.txt");
|
|
716
|
+
fs::write(&test_file, "").unwrap();
|
|
717
|
+
|
|
718
|
+
let content = r#"
|
|
719
|
+
# This is a comment
|
|
720
|
+
requests>=2.25.0
|
|
721
|
+
flask[async]>=2.0,<3.0
|
|
722
|
+
|
|
723
|
+
click==8.0.0
|
|
724
|
+
"#;
|
|
725
|
+
|
|
726
|
+
let resolved =
|
|
727
|
+
RequirementsParser::resolve_requirements_includes(content, &test_file).unwrap();
|
|
728
|
+
|
|
729
|
+
// All lines should be preserved
|
|
730
|
+
assert!(resolved.contains("# This is a comment"));
|
|
731
|
+
assert!(resolved.contains("requests>=2.25.0"));
|
|
732
|
+
assert!(resolved.contains("flask[async]>=2.0,<3.0"));
|
|
733
|
+
assert!(resolved.contains("click==8.0.0"));
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
#[test]
|
|
737
|
+
fn test_resolve_requirements_includes_multiple_directives() {
|
|
738
|
+
use std::fs;
|
|
739
|
+
use tempfile::TempDir;
|
|
740
|
+
|
|
741
|
+
let temp_dir = TempDir::new().unwrap();
|
|
742
|
+
let project_path = temp_dir.path();
|
|
743
|
+
|
|
744
|
+
// Create referenced files
|
|
745
|
+
let base_req = project_path.join("base.txt");
|
|
746
|
+
fs::write(&base_req, "flask==2.0.0\n").unwrap();
|
|
747
|
+
|
|
748
|
+
let security_req = project_path.join("security.txt");
|
|
749
|
+
fs::write(&security_req, "cryptography>=3.0\n").unwrap();
|
|
750
|
+
|
|
751
|
+
let dev_req = project_path.join("dev.txt");
|
|
752
|
+
|
|
753
|
+
let content = "-r base.txt\n-r security.txt\npytest==8.4.2\n";
|
|
754
|
+
|
|
755
|
+
let resolved =
|
|
756
|
+
RequirementsParser::resolve_requirements_includes(content, &dev_req).unwrap();
|
|
757
|
+
|
|
758
|
+
// Both includes should be resolved to absolute paths
|
|
759
|
+
let base_abs = base_req.canonicalize().unwrap();
|
|
760
|
+
let security_abs = security_req.canonicalize().unwrap();
|
|
761
|
+
|
|
762
|
+
assert!(resolved.contains(&format!("-r {}", base_abs.display())));
|
|
763
|
+
assert!(resolved.contains(&format!("-r {}", security_abs.display())));
|
|
764
|
+
assert!(resolved.contains("pytest==8.4.2"));
|
|
765
|
+
}
|
|
509
766
|
}
|
|
@@ -65,17 +65,23 @@ fn run_cli(args: Vec<String>) -> PyResult<i32> {
|
|
|
65
65
|
|
|
66
66
|
match cli.command {
|
|
67
67
|
None => {
|
|
68
|
-
let
|
|
68
|
+
let (merged_audit_args, config) = match cli.audit_args.load_and_merge_config() {
|
|
69
|
+
Ok(result) => result,
|
|
70
|
+
Err(e) => {
|
|
71
|
+
eprintln!("Configuration error: {e}");
|
|
72
|
+
return Ok(1);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
69
75
|
|
|
70
|
-
let
|
|
76
|
+
let http_config = config.as_ref().map(|c| c.http.clone()).unwrap_or_default();
|
|
77
|
+
|
|
78
|
+
let cache_dir = merged_audit_args.cache_dir.clone().unwrap_or_else(|| {
|
|
71
79
|
dirs::cache_dir()
|
|
72
80
|
.unwrap_or_else(std::env::temp_dir)
|
|
73
81
|
.join("pysentry")
|
|
74
82
|
});
|
|
75
83
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
match audit(&audit_args, &cache_dir, http_config).await {
|
|
84
|
+
match audit(&merged_audit_args, &cache_dir, http_config).await {
|
|
79
85
|
Ok(exit_code) => Ok(exit_code),
|
|
80
86
|
Err(e) => {
|
|
81
87
|
eprintln!("Error: Audit failed: {e}");
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pysentry_rs-0.3.11 → pysentry_rs-0.3.12}/fixtures/requirements-tests-vulnerable/requirements.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|