drayage 0.2.6__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 (53) hide show
  1. drayage-0.2.6/.dockerignore +17 -0
  2. drayage-0.2.6/.github/dependabot.yml +21 -0
  3. drayage-0.2.6/.github/workflows/ci.yml +61 -0
  4. drayage-0.2.6/.github/workflows/live-validation.yml +45 -0
  5. drayage-0.2.6/.github/workflows/oci-conformance.yml +101 -0
  6. drayage-0.2.6/.github/workflows/real-registry.yml +39 -0
  7. drayage-0.2.6/.github/workflows/release.yml +270 -0
  8. drayage-0.2.6/.gitignore +4 -0
  9. drayage-0.2.6/.pre-commit-config.yaml +78 -0
  10. drayage-0.2.6/CHANGELOG.md +76 -0
  11. drayage-0.2.6/Cargo.lock +2722 -0
  12. drayage-0.2.6/Cargo.toml +40 -0
  13. drayage-0.2.6/Dockerfile +25 -0
  14. drayage-0.2.6/LICENSE +21 -0
  15. drayage-0.2.6/Makefile +35 -0
  16. drayage-0.2.6/PKG-INFO +17 -0
  17. drayage-0.2.6/README.md +272 -0
  18. drayage-0.2.6/SECURITY.md +30 -0
  19. drayage-0.2.6/config.example.toml +27 -0
  20. drayage-0.2.6/deploy/caddy/Caddyfile +15 -0
  21. drayage-0.2.6/deploy/docker-compose.yml +37 -0
  22. drayage-0.2.6/deploy/nginx/drayage.conf +28 -0
  23. drayage-0.2.6/deploy/systemd/drayage.service +39 -0
  24. drayage-0.2.6/docs/oci-conformance.md +75 -0
  25. drayage-0.2.6/docs/production.md +123 -0
  26. drayage-0.2.6/docs/reverse-proxy.md +53 -0
  27. drayage-0.2.6/docs/troubleshooting.md +89 -0
  28. drayage-0.2.6/docs/upgrades.md +79 -0
  29. drayage-0.2.6/drayage/__init__.py +10 -0
  30. drayage-0.2.6/drayage/__main__.py +49 -0
  31. drayage-0.2.6/drayage/py.typed +0 -0
  32. drayage-0.2.6/pyproject.toml +33 -0
  33. drayage-0.2.6/scripts/install_systemd.sh +99 -0
  34. drayage-0.2.6/scripts/resolve_oci_pull_fixture.py +79 -0
  35. drayage-0.2.6/scripts/run_oci_pull_conformance.sh +72 -0
  36. drayage-0.2.6/src/cache.rs +1428 -0
  37. drayage-0.2.6/src/config.rs +955 -0
  38. drayage-0.2.6/src/gen_ca.rs +183 -0
  39. drayage-0.2.6/src/handlers/blobs.rs +231 -0
  40. drayage-0.2.6/src/handlers/manifests.rs +662 -0
  41. drayage-0.2.6/src/handlers/mod.rs +3 -0
  42. drayage-0.2.6/src/handlers/version.rs +11 -0
  43. drayage-0.2.6/src/init.rs +407 -0
  44. drayage-0.2.6/src/lib.rs +160 -0
  45. drayage-0.2.6/src/main.rs +315 -0
  46. drayage-0.2.6/src/metrics.rs +1121 -0
  47. drayage-0.2.6/src/negative_cache.rs +166 -0
  48. drayage-0.2.6/src/registry.rs +320 -0
  49. drayage-0.2.6/src/server.rs +345 -0
  50. drayage-0.2.6/src/tls.rs +241 -0
  51. drayage-0.2.6/src/upstream.rs +1934 -0
  52. drayage-0.2.6/tests/integration.rs +3003 -0
  53. drayage-0.2.6/tests/real_registry.rs +395 -0
@@ -0,0 +1,17 @@
1
+ .git
2
+ .github
3
+ .pre-commit-config.yaml
4
+ target
5
+ target2
6
+ dist
7
+ build
8
+ .venv
9
+ __pycache__
10
+ *.pyc
11
+ *.pyo
12
+ *.pyd
13
+ .DS_Store
14
+ cache
15
+ certs
16
+ registry
17
+ config.toml
@@ -0,0 +1,21 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: cargo
4
+ directory: /
5
+ schedule:
6
+ interval: weekly
7
+
8
+ - package-ecosystem: pip
9
+ directory: /
10
+ schedule:
11
+ interval: weekly
12
+
13
+ - package-ecosystem: docker
14
+ directory: /
15
+ schedule:
16
+ interval: weekly
17
+
18
+ - package-ecosystem: github-actions
19
+ directory: /
20
+ schedule:
21
+ interval: weekly
@@ -0,0 +1,61 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ concurrency:
10
+ group: ${{ github.workflow }}-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ jobs:
14
+ lint:
15
+ name: Lint
16
+ runs-on: ubuntu-latest
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+
20
+ - name: Install Rust
21
+ uses: dtolnay/rust-toolchain@stable
22
+ with:
23
+ components: rustfmt, clippy
24
+
25
+ - name: Run linting
26
+ run: make lint
27
+
28
+ test:
29
+ name: Test
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v4
33
+
34
+ - name: Install Rust
35
+ uses: dtolnay/rust-toolchain@stable
36
+
37
+ - name: Run tests
38
+ run: make test
39
+
40
+ docker-build:
41
+ name: Docker Build
42
+ runs-on: ubuntu-latest
43
+ steps:
44
+ - uses: actions/checkout@v4
45
+
46
+ - name: Build container image
47
+ run: make docker-build
48
+
49
+ all-checks-passed:
50
+ name: All checks passed
51
+ runs-on: ubuntu-latest
52
+ needs: [lint, test, docker-build]
53
+ if: always()
54
+ steps:
55
+ - name: Verify all checks passed
56
+ run: |
57
+ if [ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" == "true" ]; then
58
+ echo "Some checks failed or were cancelled"
59
+ exit 1
60
+ fi
61
+ echo "All checks passed"
@@ -0,0 +1,45 @@
1
+ name: Live Validation
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ run_real_registry:
7
+ description: "Run live upstream registry tests"
8
+ type: boolean
9
+ default: true
10
+ run_oci_pull_conformance:
11
+ description: "Run OCI pull conformance"
12
+ type: boolean
13
+ default: true
14
+ schedule:
15
+ - cron: "17 3 * * *"
16
+
17
+ concurrency:
18
+ group: live-validation
19
+ cancel-in-progress: true
20
+
21
+ permissions:
22
+ contents: read
23
+
24
+ jobs:
25
+ real-registry:
26
+ if: ${{ github.event_name != 'workflow_dispatch' || inputs.run_real_registry }}
27
+ uses: ./.github/workflows/real-registry.yml
28
+
29
+ oci-pull-conformance:
30
+ if: ${{ github.event_name != 'workflow_dispatch' || inputs.run_oci_pull_conformance }}
31
+ uses: ./.github/workflows/oci-conformance.yml
32
+
33
+ all-checks-passed:
34
+ name: Live validation passed
35
+ runs-on: ubuntu-latest
36
+ needs: [real-registry, oci-pull-conformance]
37
+ if: always()
38
+ steps:
39
+ - name: Verify scheduled validation jobs
40
+ run: |
41
+ if [ "${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}" = "true" ]; then
42
+ echo "One or more live validation jobs failed or were cancelled"
43
+ exit 1
44
+ fi
45
+ echo "Live validation jobs completed successfully"
@@ -0,0 +1,101 @@
1
+ name: OCI Pull Conformance
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ workflow_call:
6
+
7
+ permissions:
8
+ contents: read
9
+
10
+ concurrency:
11
+ group: oci-pull-conformance-${{ github.ref }}
12
+ cancel-in-progress: true
13
+
14
+ jobs:
15
+ pull:
16
+ name: Pull Conformance
17
+ runs-on: ubuntu-latest
18
+ timeout-minutes: 20
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - name: Install Rust
23
+ uses: dtolnay/rust-toolchain@stable
24
+
25
+ - name: Build Drayage
26
+ run: cargo build --locked
27
+
28
+ - name: Prepare conformance config
29
+ run: |
30
+ mkdir -p /tmp/drayage-conformance-cache
31
+ cat > /tmp/drayage-conformance.toml <<'EOF'
32
+ listen = "127.0.0.1:5001"
33
+ cache_dir = "/tmp/drayage-conformance-cache"
34
+ manifest_ttl_seconds = 900
35
+ negative_ttl_seconds = 60
36
+ connect_timeout_secs = 10
37
+ read_timeout_secs = 30
38
+
39
+ [upstreams.registry_k8s_io]
40
+ registry = "https://registry.k8s.io"
41
+ EOF
42
+
43
+ - name: Start Drayage
44
+ run: |
45
+ target/debug/drayage serve --config /tmp/drayage-conformance.toml --log-json \
46
+ > /tmp/drayage-conformance.log 2>&1 &
47
+ echo $! > /tmp/drayage-conformance.pid
48
+
49
+ - name: Wait for readiness
50
+ run: |
51
+ for _ in $(seq 1 60); do
52
+ if curl -fsS http://127.0.0.1:5001/readyz >/dev/null; then
53
+ exit 0
54
+ fi
55
+ sleep 1
56
+ done
57
+ cat /tmp/drayage-conformance.log
58
+ exit 1
59
+
60
+ - name: Resolve pull fixture
61
+ id: fixture
62
+ run: |
63
+ python3 scripts/resolve_oci_pull_fixture.py \
64
+ --root-url http://127.0.0.1:5001 \
65
+ --namespace registry.k8s.io/pause \
66
+ --reference 3.9 >> "$GITHUB_OUTPUT"
67
+
68
+ - name: Run official OCI Distribution pull conformance
69
+ uses: opencontainers/distribution-spec@v1.1.1
70
+ env:
71
+ OCI_ROOT_URL: http://127.0.0.1:5001
72
+ OCI_NAMESPACE: registry.k8s.io/pause
73
+ OCI_TAG_NAME: 3.9
74
+ OCI_MANIFEST_DIGEST: ${{ steps.fixture.outputs.manifest_digest }}
75
+ OCI_BLOB_DIGEST: ${{ steps.fixture.outputs.blob_digest }}
76
+ OCI_TEST_PULL: 1
77
+ OCI_HIDE_SKIPPED_WORKFLOWS: 1
78
+ OCI_DEBUG: 0
79
+ OCI_REPORT_DIR: ${{ github.workspace }}/oci-conformance-results
80
+
81
+ - name: Upload conformance artifacts
82
+ if: always()
83
+ uses: actions/upload-artifact@v4
84
+ with:
85
+ name: oci-pull-conformance-report
86
+ path: |
87
+ oci-conformance-results/junit.xml
88
+ oci-conformance-results/report.html
89
+ if-no-files-found: warn
90
+ retention-days: 14
91
+
92
+ - name: Print Drayage log
93
+ if: always()
94
+ run: cat /tmp/drayage-conformance.log
95
+
96
+ - name: Stop Drayage
97
+ if: always()
98
+ run: |
99
+ if [ -f /tmp/drayage-conformance.pid ]; then
100
+ kill "$(cat /tmp/drayage-conformance.pid)" || true
101
+ fi
@@ -0,0 +1,39 @@
1
+ name: Real Registry Live Tests
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ workflow_call:
6
+
7
+ concurrency:
8
+ group: real-registry-${{ github.ref }}
9
+ cancel-in-progress: true
10
+
11
+ permissions:
12
+ contents: read
13
+
14
+ jobs:
15
+ real-registry:
16
+ name: Real Registry
17
+ runs-on: ubuntu-latest
18
+ timeout-minutes: 20
19
+ steps:
20
+ - uses: actions/checkout@v4
21
+
22
+ - name: Install Rust
23
+ uses: dtolnay/rust-toolchain@stable
24
+
25
+ - name: Run live registry tests
26
+ run: |
27
+ mkdir -p live-test-artifacts
28
+ set -o pipefail
29
+ cargo test --locked --test real_registry -- --ignored --test-threads=1 \
30
+ 2>&1 | tee live-test-artifacts/real-registry.log
31
+
32
+ - name: Upload live registry artifacts
33
+ if: always()
34
+ uses: actions/upload-artifact@v4
35
+ with:
36
+ name: real-registry-log
37
+ path: live-test-artifacts/real-registry.log
38
+ if-no-files-found: warn
39
+ retention-days: 14
@@ -0,0 +1,270 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v[0-9]+.[0-9]+.[0-9]+"
7
+ workflow_dispatch:
8
+ inputs:
9
+ dry_run:
10
+ description: "Dry run (skip publishing)"
11
+ type: boolean
12
+ default: true
13
+ skip_crates_io:
14
+ description: "Skip crates.io publish"
15
+ type: boolean
16
+ default: false
17
+ skip_pypi:
18
+ description: "Skip PyPI publish"
19
+ type: boolean
20
+ default: false
21
+
22
+ permissions:
23
+ contents: write
24
+ packages: write
25
+
26
+ concurrency:
27
+ group: release
28
+ cancel-in-progress: false
29
+
30
+ jobs:
31
+ test:
32
+ name: Test
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+
37
+ - name: Install Rust
38
+ uses: dtolnay/rust-toolchain@stable
39
+
40
+ - name: Run tests
41
+ run: make test
42
+
43
+ real-registry:
44
+ name: Real Registry
45
+ uses: ./.github/workflows/real-registry.yml
46
+
47
+ oci-pull-conformance:
48
+ name: OCI Pull Conformance
49
+ uses: ./.github/workflows/oci-conformance.yml
50
+
51
+ build-wheels:
52
+ name: Build wheel (${{ matrix.target }})
53
+ needs: [test, real-registry, oci-pull-conformance]
54
+ runs-on: ${{ matrix.os }}
55
+ strategy:
56
+ matrix:
57
+ include:
58
+ - target: x86_64-unknown-linux-gnu
59
+ os: ubuntu-latest
60
+ manylinux: manylinux_2_28
61
+ - target: aarch64-unknown-linux-gnu
62
+ os: ubuntu-latest
63
+ manylinux: manylinux_2_28
64
+ - target: x86_64-apple-darwin
65
+ os: macos-14
66
+ - target: aarch64-apple-darwin
67
+ os: macos-14
68
+ steps:
69
+ - uses: actions/checkout@v4
70
+
71
+ - name: Build wheel
72
+ uses: PyO3/maturin-action@v1
73
+ id: build
74
+ continue-on-error: true
75
+ with:
76
+ target: ${{ matrix.target }}
77
+ args: --release --strip --out dist
78
+ manylinux: ${{ matrix.manylinux || 'auto' }}
79
+
80
+ - name: Retry build wheel
81
+ if: steps.build.outcome == 'failure'
82
+ uses: PyO3/maturin-action@v1
83
+ with:
84
+ target: ${{ matrix.target }}
85
+ args: --release --strip --out dist
86
+ manylinux: ${{ matrix.manylinux || 'auto' }}
87
+ env:
88
+ CARGO_INCREMENTAL: "0"
89
+
90
+ - name: Upload wheel
91
+ uses: actions/upload-artifact@v4
92
+ with:
93
+ name: wheel-${{ matrix.target }}
94
+ path: dist/*.whl
95
+
96
+ sdist:
97
+ name: Build source distribution
98
+ needs: [test, real-registry, oci-pull-conformance]
99
+ runs-on: ubuntu-latest
100
+ steps:
101
+ - uses: actions/checkout@v4
102
+
103
+ - name: Build sdist
104
+ uses: PyO3/maturin-action@v1
105
+ with:
106
+ command: sdist
107
+ args: --out dist
108
+
109
+ - name: Upload sdist
110
+ uses: actions/upload-artifact@v4
111
+ with:
112
+ name: sdist
113
+ path: dist/*.tar.gz
114
+
115
+ container:
116
+ name: Build container image
117
+ needs: [test, real-registry, oci-pull-conformance]
118
+ runs-on: ubuntu-latest
119
+ steps:
120
+ - uses: actions/checkout@v4
121
+
122
+ - name: Set up QEMU
123
+ uses: docker/setup-qemu-action@v3
124
+
125
+ - name: Set up Docker Buildx
126
+ uses: docker/setup-buildx-action@v3
127
+
128
+ - name: Login to GHCR
129
+ if: ${{ inputs.dry_run != true && startsWith(github.ref, 'refs/tags/v') }}
130
+ uses: docker/login-action@v3
131
+ with:
132
+ registry: ghcr.io
133
+ username: ${{ github.actor }}
134
+ password: ${{ secrets.GITHUB_TOKEN }}
135
+
136
+ - name: Docker metadata
137
+ id: meta
138
+ uses: docker/metadata-action@v5
139
+ with:
140
+ images: ghcr.io/${{ github.repository }}
141
+ tags: |
142
+ type=ref,event=tag
143
+ type=semver,pattern={{version}}
144
+ type=raw,value=dry-run,enable=${{ !startsWith(github.ref, 'refs/tags/v') }}
145
+
146
+ - name: Build and optionally push image
147
+ uses: docker/build-push-action@v6
148
+ with:
149
+ context: .
150
+ platforms: linux/amd64,linux/arm64
151
+ push: ${{ inputs.dry_run != true && startsWith(github.ref, 'refs/tags/v') }}
152
+ tags: ${{ steps.meta.outputs.tags }}
153
+ labels: ${{ steps.meta.outputs.labels }}
154
+
155
+ release:
156
+ name: Publish
157
+ needs: [build-wheels, sdist, container]
158
+ runs-on: ubuntu-latest
159
+ steps:
160
+ - uses: actions/checkout@v4
161
+
162
+ - name: Install Rust
163
+ uses: dtolnay/rust-toolchain@stable
164
+
165
+ - name: Download wheel artifacts
166
+ uses: actions/download-artifact@v4
167
+ with:
168
+ pattern: wheel-*
169
+ path: ${{ runner.temp }}/artifacts
170
+
171
+ - name: Download sdist artifact
172
+ uses: actions/download-artifact@v4
173
+ with:
174
+ pattern: sdist
175
+ path: ${{ runner.temp }}/artifacts
176
+
177
+ - name: Publish to crates.io
178
+ if: ${{ inputs.dry_run != true && inputs.skip_crates_io != true }}
179
+ env:
180
+ CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
181
+ run: cargo publish --locked
182
+
183
+ - name: Collect PyPI artifacts
184
+ if: ${{ inputs.dry_run != true && inputs.skip_pypi != true }}
185
+ run: |
186
+ mkdir -p ${{ runner.temp }}/pypi-dist
187
+ cp ${{ runner.temp }}/artifacts/wheel-*/*.whl ${{ runner.temp }}/pypi-dist/
188
+ cp ${{ runner.temp }}/artifacts/sdist/*.tar.gz ${{ runner.temp }}/pypi-dist/
189
+
190
+ - name: Publish to PyPI
191
+ if: ${{ inputs.dry_run != true && inputs.skip_pypi != true }}
192
+ uses: pypa/gh-action-pypi-publish@release/v1
193
+ with:
194
+ password: ${{ secrets.PYPI_API_TOKEN }}
195
+ packages-dir: ${{ runner.temp }}/pypi-dist/
196
+
197
+ - name: Create GitHub Release
198
+ if: ${{ inputs.dry_run != true && startsWith(github.ref, 'refs/tags/v') }}
199
+ uses: softprops/action-gh-release@v2
200
+ with:
201
+ generate_release_notes: true
202
+ files: |
203
+ ${{ runner.temp }}/artifacts/wheel-*/*.whl
204
+ ${{ runner.temp }}/artifacts/sdist/*.tar.gz
205
+
206
+ - name: Dry run summary
207
+ if: ${{ inputs.dry_run == true }}
208
+ run: |
209
+ echo "Dry run - would have published:"
210
+ find "$RUNNER_TEMP/artifacts" -type f | sort
211
+
212
+ verify:
213
+ name: Verify
214
+ needs: [release]
215
+ if: ${{ inputs.dry_run != true && startsWith(github.ref, 'refs/tags/v') }}
216
+ runs-on: ubuntu-latest
217
+ steps:
218
+ - name: Verify crates.io
219
+ if: ${{ inputs.skip_crates_io != true }}
220
+ run: |
221
+ version="${GITHUB_REF_NAME#v}"
222
+ for attempt in 1 2 3 4 5; do
223
+ status=$(curl -s -o /dev/null -w '%{http_code}' \
224
+ -H "User-Agent: drayage-release-verify" \
225
+ "https://crates.io/api/v1/crates/drayage/${version}")
226
+ if [ "$status" = "200" ]; then
227
+ echo "drayage ${version} verified on crates.io (attempt ${attempt})"
228
+ exit 0
229
+ fi
230
+ echo "Attempt ${attempt}: crates.io returned HTTP ${status}, retrying in 15s..."
231
+ sleep 15
232
+ done
233
+ echo "::error::drayage ${version} not found on crates.io after 5 attempts"
234
+ exit 1
235
+
236
+ - name: Verify PyPI
237
+ if: ${{ inputs.skip_pypi != true }}
238
+ run: |
239
+ version="${GITHUB_REF_NAME#v}"
240
+ for attempt in 1 2 3 4 5; do
241
+ status=$(curl -s -o /dev/null -w '%{http_code}' \
242
+ "https://pypi.org/pypi/drayage/${version}/json")
243
+ if [ "$status" = "200" ]; then
244
+ echo "drayage ${version} verified on PyPI (attempt ${attempt})"
245
+ exit 0
246
+ fi
247
+ echo "Attempt ${attempt}: PyPI returned HTTP ${status}, retrying in 15s..."
248
+ sleep 15
249
+ done
250
+ echo "::error::drayage ${version} not found on PyPI after 5 attempts"
251
+ exit 1
252
+
253
+ - name: Verify GHCR
254
+ run: |
255
+ version="${GITHUB_REF_NAME#v}"
256
+ for attempt in 1 2 3 4 5; do
257
+ token=$(curl -s "https://ghcr.io/token?scope=repository:rvben/drayage:pull" | jq -r .token)
258
+ status=$(curl -s -o /dev/null -w '%{http_code}' \
259
+ -H "Authorization: Bearer ${token}" \
260
+ -H "Accept: application/vnd.oci.image.index.v1+json" \
261
+ "https://ghcr.io/v2/rvben/drayage/manifests/${version}")
262
+ if [ "$status" = "200" ]; then
263
+ echo "drayage ${version} verified on GHCR (attempt ${attempt})"
264
+ exit 0
265
+ fi
266
+ echo "Attempt ${attempt}: GHCR returned HTTP ${status}, retrying in 15s..."
267
+ sleep 15
268
+ done
269
+ echo "::error::drayage ${version} not found on GHCR after 5 attempts"
270
+ exit 1
@@ -0,0 +1,4 @@
1
+ /target
2
+ /target2
3
+ /registry
4
+ /oci-conformance-results
@@ -0,0 +1,78 @@
1
+ fail_fast: true
2
+
3
+ repos:
4
+ # Secret detection
5
+ - repo: https://github.com/gitleaks/gitleaks
6
+ rev: v8.30.0
7
+ hooks:
8
+ - id: gitleaks
9
+
10
+ # Conventional Commits validation
11
+ - repo: https://github.com/compilerla/conventional-pre-commit
12
+ rev: v3.6.0
13
+ hooks:
14
+ - id: conventional-pre-commit
15
+ stages: [commit-msg]
16
+
17
+ # Rust formatting and linting
18
+ - repo: local
19
+ hooks:
20
+ - id: cargo-fmt
21
+ name: cargo fmt
22
+ entry: cargo fmt --all --
23
+ language: system
24
+ types: [rust]
25
+ pass_filenames: false
26
+
27
+ - id: cargo-clippy
28
+ name: cargo clippy
29
+ entry: cargo clippy -- -D warnings
30
+ language: system
31
+ types: [rust]
32
+ pass_filenames: false
33
+
34
+ - id: cargo-lock-check
35
+ name: check Cargo.lock is up-to-date
36
+ entry: sh -c 'cargo metadata --locked --format-version 1 --no-deps >/dev/null'
37
+ language: system
38
+ files: ^Cargo\.(toml|lock)$
39
+ pass_filenames: false
40
+
41
+ # General file quality checks
42
+ - repo: builtin
43
+ hooks:
44
+ - id: trailing-whitespace
45
+ - id: end-of-file-fixer
46
+ - id: check-yaml
47
+ - id: check-toml
48
+ - id: check-merge-conflict
49
+ - id: check-case-conflict
50
+ - id: mixed-line-ending
51
+ args: [--fix=lf]
52
+
53
+ # Pre-push hooks
54
+ - repo: local
55
+ hooks:
56
+ - id: cargo-test
57
+ name: cargo test
58
+ entry: make test
59
+ language: system
60
+ types: [rust]
61
+ pass_filenames: false
62
+ stages: [pre-push]
63
+
64
+ - id: cargo-lint
65
+ name: cargo clippy (all targets)
66
+ entry: make lint
67
+ language: system
68
+ types: [rust]
69
+ pass_filenames: false
70
+ stages: [pre-push]
71
+
72
+ - id: cargo-lock-check-push
73
+ name: verify Cargo.lock is committed and up-to-date
74
+ entry: sh -c 'cargo metadata --locked --format-version 1 --no-deps >/dev/null'
75
+ language: system
76
+ files: ^Cargo\.(toml|lock)$
77
+ pass_filenames: false
78
+ stages: [pre-push]