Skip to content

Commit 5f189fa

Browse files
authored
Merge pull request #856 from Manuelshub/feat/deployment-rollback
fix(testing): stabilize test optimizer suite and fix CI clippy checks
2 parents 0ab69bf + a4d4436 commit 5f189fa

102 files changed

Lines changed: 1972 additions & 1101 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.cargo/config.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# MSRV-aware dependency resolution.
2+
#
3+
# The crate declares `rust-version = "1.80"` and CI enforces it via the
4+
# `MSRV (Rust 1.80)` job (`cargo check --locked --workspace` on 1.80.0).
5+
# Without this setting, `cargo update` happily resolves to dependency
6+
# versions that require a newer compiler (e.g. crates that switched to
7+
# edition 2024, which needs Rust 1.85), which breaks that job even though
8+
# nothing in our own source changed.
9+
#
10+
# `fallback` tells the resolver to prefer the newest dependency version
11+
# whose own `rust-version` is compatible with ours, and only consider
12+
# incompatible ones when there is no other choice.
13+
# Stabilized in Cargo 1.84.
14+
[resolver]
15+
incompatible-rust-versions = "fallback"

.github/workflows/benchmark-latency.yml

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ on:
2929
# Allow manual trigger for ad-hoc budget checks.
3030
workflow_dispatch:
3131

32+
permissions:
33+
contents: read
34+
pull-requests: write
35+
3236
jobs:
3337
latency-budget:
3438
name: Latency Budget Check
@@ -51,10 +55,14 @@ jobs:
5155
# while still exercising cold-start and command-dispatch paths.
5256
- name: Run latency benchmarks
5357
run: |
58+
mkdir -p target/criterion
59+
# Criterion's harness takes a SINGLE positional filter, which it
60+
# treats as a regex. Passing the three group names as separate
61+
# arguments made it exit 2 with
62+
# "unexpected argument 'cli_command_latency' found", so this step
63+
# failed before any budget was ever measured. Use one alternation.
5464
cargo bench --locked -- \
55-
cli_cold_start \
56-
cli_command_latency \
57-
latency_budget \
65+
'cli_cold_start|cli_command_latency|latency_budget' \
5866
2>&1 | tee target/criterion/latency-bench-output.txt
5967
6068
# Parse Criterion output and check against latency budgets.
@@ -65,17 +73,24 @@ jobs:
6573
id: budget-check
6674
run: |
6775
chmod +x scripts/check-latency-budgets.sh
76+
# Capture stdout only. The script writes its JSON report to stdout and
77+
# progress notes to stderr; folding stderr in with 2>&1 made `jq` fail
78+
# with "Invalid numeric literal". Because the result was then compared
79+
# only against the literal "false", an unparseable report silently
80+
# counted as a pass -- this gate could not fail. Let stderr flow to the
81+
# job log instead.
6882
REPORT=$(bash scripts/check-latency-budgets.sh \
6983
--input target/criterion/latency-bench-output.txt \
70-
--report-path target/criterion/latency-budget-report.json \
71-
2>&1) || EXIT_CODE=$?
84+
--report-path target/criterion/latency-budget-report.json) || EXIT_CODE=$?
7285
echo "::group::Latency Budget Report"
7386
echo "$REPORT"
7487
echo "::endgroup::"
75-
ALL_PASS=$(echo "$REPORT" | jq -r '.all_pass')
88+
ALL_PASS=$(printf '%s' "$REPORT" | jq -r '.all_pass' 2>/dev/null || echo "unparseable")
7689
echo "all_pass=$ALL_PASS" >> "$GITHUB_OUTPUT"
77-
if [ "$ALL_PASS" = "false" ]; then
78-
echo "❌ Latency budget violations detected!"
90+
# Fail closed: only an explicit `true` is a pass. A missing or
91+
# unparseable report is a failure, not a free pass.
92+
if [ "$ALL_PASS" != "true" ]; then
93+
echo "❌ Latency budget check did not pass (all_pass=$ALL_PASS)"
7994
echo "failures=true" >> "$GITHUB_OUTPUT"
8095
exit 1
8196
fi
@@ -94,8 +109,14 @@ jobs:
94109
target/criterion/latency-budget-report.json
95110
96111
# Post a PR comment with the budget check summary when run on a PR.
112+
# Posting the summary is a courtesy, not the gate: the budget check step
113+
# above is what fails the job. A `pull_request` from a fork gets a
114+
# read-only GITHUB_TOKEN, so createComment returns 403
115+
# ("Resource not accessible by integration"). That used to fail this job
116+
# even when every budget passed.
97117
- name: Comment PR with budget summary
98118
if: github.event_name == 'pull_request' && always()
119+
continue-on-error: true
99120
uses: actions/github-script@v7
100121
with:
101122
script: |
@@ -117,9 +138,18 @@ jobs:
117138
} else {
118139
summary += '_No latency budget report was generated._';
119140
}
120-
github.rest.issues.createComment({
121-
issue_number: context.issue.number,
122-
owner: context.repo.owner,
123-
repo: context.repo.repo,
124-
body: summary
125-
});
141+
try {
142+
await github.rest.issues.createComment({
143+
issue_number: context.issue.number,
144+
owner: context.repo.owner,
145+
repo: context.repo.repo,
146+
body: summary
147+
});
148+
} catch (error) {
149+
core.warning(
150+
`Could not post the latency budget comment (this is expected ` +
151+
`for pull requests from forks, which get a read-only token): ` +
152+
`${error.message}`
153+
);
154+
core.summary.addRaw(summary).write();
155+
}

.github/workflows/fuzzing.yml

Lines changed: 73 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ on:
1818
- 'tests/contract_property_tests.rs'
1919
- '.github/workflows/fuzzing.yml'
2020
- 'Cargo.toml'
21+
# Nightly, so the AddressSanitizer smoke stage still runs regularly without
22+
# sitting on the critical path of every pull request.
23+
schedule:
24+
- cron: '0 3 * * *'
25+
2126
# Allow manual dispatch with configurable fuzz duration.
2227
workflow_dispatch:
2328
inputs:
@@ -110,28 +115,33 @@ jobs:
110115
run: cargo build --locked
111116

112117
# ── 3. Short fuzz runs (sanity / smoke) ─────────────────────────────────────
118+
#
119+
# Deliberately a single job rather than a matrix. This stage previously fanned
120+
# out to one job per target, and each job independently rebuilt the entire
121+
# dependency tree under AddressSanitizer before it could fuzz anything. Those
122+
# jobs were skipped for the whole life of this workflow (they `needs:
123+
# fuzz-build`, which was failing), so the cost was never observed; the first
124+
# run that reached them had all 13 killed with SIGTERM after 16-23 minutes,
125+
# still compiling, without a single target ever being fuzzed.
126+
#
127+
# `cargo fuzz build` compiles every target in one pass, so the sanitizer build
128+
# is paid once and each target then runs from the built binary.
113129
fuzz-smoke:
114-
name: Fuzz Smoke Run (${{ matrix.target }})
130+
name: Fuzz Smoke Run
115131
runs-on: ubuntu-latest
116132
needs: fuzz-build
117-
strategy:
118-
fail-fast: false
119-
matrix:
120-
target:
121-
- fuzz_validate_public_key
122-
- fuzz_validate_secret_key
123-
- fuzz_validate_contract_id
124-
- fuzz_validate_wallet_name
125-
- fuzz_validate_amount
126-
- fuzz_passphrase_strength
127-
- fuzz_wasm_hash
128-
- fuzz_encrypted_bundle_parse
129-
- fuzz_template_operations
130-
# Contract fuzzing harnesses
131-
- fuzz_wasm_validation
132-
- fuzz_contract_invocation
133-
- fuzz_contract_spec_parse
134-
- fuzz_test_generator
133+
# Not on pull requests. Instrumenting this dependency tree with
134+
# AddressSanitizer and then compiling `starforge` -- one very large crate --
135+
# exceeds the memory of a standard 16 GB runner, and the job is killed
136+
# part-way through the build with no diagnostic in the log. Capping build
137+
# parallelism only moved the failure from the leaf crates to `starforge`
138+
# itself, and disk was never the constraint (85 GB free).
139+
#
140+
# `Build Fuzz Harnesses` still compiles every harness on each PR, so a
141+
# harness that stops building is caught there. What moves off the PR path is
142+
# only the act of running them, which now happens nightly and on demand.
143+
if: github.event_name != 'pull_request'
144+
timeout-minutes: 90
135145
steps:
136146
- uses: actions/checkout@v4
137147

@@ -155,23 +165,59 @@ jobs:
155165
~/.cargo/registry
156166
~/.cargo/git
157167
fuzz/target
158-
key: ${{ runner.os }}-fuzz-smoke-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }}
168+
key: ${{ runner.os }}-fuzz-smoke-${{ hashFiles('fuzz/Cargo.lock') }}
159169
restore-keys: |
160-
${{ runner.os }}-fuzz-smoke-${{ matrix.target }}-
170+
${{ runner.os }}-fuzz-smoke-
171+
172+
- name: Build all fuzz targets
173+
env:
174+
# Fewer parallel rustc processes, each holding less peak memory.
175+
CARGO_BUILD_JOBS: '2'
176+
run: cargo fuzz build --fuzz-dir fuzz
161177

162-
- name: Run fuzz target (${{ matrix.target }})
178+
- name: Run each fuzz target
163179
run: |
164180
DURATION=${{ github.event.inputs.fuzz_duration || '30' }}
165-
cargo fuzz run ${{ matrix.target }} \
166-
--fuzz-dir fuzz \
167-
-- -max_total_time=${DURATION} -max_len=4096
181+
TARGETS="
182+
fuzz_validate_public_key
183+
fuzz_validate_secret_key
184+
fuzz_validate_contract_id
185+
fuzz_validate_wallet_name
186+
fuzz_validate_amount
187+
fuzz_passphrase_strength
188+
fuzz_wasm_hash
189+
fuzz_encrypted_bundle_parse
190+
fuzz_template_operations
191+
fuzz_wasm_validation
192+
fuzz_contract_invocation
193+
fuzz_contract_spec_parse
194+
fuzz_test_generator
195+
"
196+
failed=""
197+
for target in $TARGETS; do
198+
echo "::group::$target"
199+
# The build above is already done, so this only fuzzes.
200+
if cargo fuzz run "$target" --fuzz-dir fuzz \
201+
-- -max_total_time="$DURATION" -max_len=4096; then
202+
echo " PASS $target"
203+
else
204+
echo " FAIL $target"
205+
failed="$failed $target"
206+
fi
207+
echo "::endgroup::"
208+
done
209+
if [ -n "$failed" ]; then
210+
echo "Fuzz targets reported findings:$failed"
211+
exit 1
212+
fi
213+
echo "All fuzz targets completed without findings."
168214
169215
- name: Upload corpus artifacts on failure
170216
if: failure()
171217
uses: actions/upload-artifact@v4
172218
with:
173-
name: fuzz-corpus-${{ matrix.target }}-${{ github.run_id }}
174-
path: fuzz/corpus/${{ matrix.target }}/
219+
name: fuzz-corpus-${{ github.run_id }}
220+
path: fuzz/corpus/
175221

176222
# ── 4. Coverage reporting ─────────────────────────────────────────────────────
177223
coverage:

.gitignore

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,13 @@ target/debug/incremental/
146146
.rustfmt.toml.local
147147

148148
# Cargo
149-
.cargo/
149+
# Ignore local cargo state, but keep the committed resolver config:
150+
# .cargo/config.toml pins MSRV-aware dependency resolution so that a
151+
# future `cargo update` cannot silently break the Rust 1.80 CI job.
152+
# (Uses `.cargo/*` rather than `.cargo/`: git cannot re-include a file
153+
# whose parent directory is itself excluded.)
154+
.cargo/*
155+
!.cargo/config.toml
150156
Cargo.toml.local
151157

152158
# GitHub Actions

0 commit comments

Comments
 (0)