-
Notifications
You must be signed in to change notification settings - Fork 385
Expand file tree
/
Copy pathlocal-ci.sh
More file actions
executable file
Β·227 lines (192 loc) Β· 9.19 KB
/
Copy pathlocal-ci.sh
File metadata and controls
executable file
Β·227 lines (192 loc) Β· 9.19 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#!/bin/bash
# local-ci.sh - Reproduce the CI pipeline locally
#
# This script mirrors the 5-job pipeline in .github/workflows/ci-cd.yml:
#
# 1. check - cargo fmt + clippy
# 2. soroban-checks - stellar contract build + optimize + inspect
# 3. build-and-test - cargo build + unit/integration tests
# 4. audit - cargo audit
# 5. coverage - cargo tarpaulin + enforce_coverage.py
#
# Run individual sections via the --only flag (see "USAGE" below), or run the
# whole pipeline with no arguments.
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Project directory
PROJECT_DIR="stellar-lend"
# βββ USAGE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
usage() {
cat <<EOF
Usage: $(basename "$0") [--only <section>] [--help]
Sections (each maps to one CI job):
check cargo fmt --all -- --check + cargo clippy
soroban-checks stellar contract build + optimize + inspect
build-and-test cargo build + unit/integration tests
audit cargo audit
coverage cargo tarpaulin + enforce_coverage.py
all (default) run every section in pipeline order
NOTE: ci-cd.yml's build-and-test job runs on macos-latest. If you're on Linux,
this script still runs the same commands; expect occasional platform-specific
flakes from native dev-dependencies (e.g. ed25519-dalek, rand).
EOF
}
ONLY=""
while [ $# -gt 0 ]; do
case "$1" in
--only) ONLY="$2"; shift 2 ;;
--help|-h) usage; exit 0 ;;
*) echo "unknown arg: $1"; usage; exit 1 ;;
esac
done
should_run() {
[ -z "$ONLY" ] || [ "$ONLY" = "$1" ] || [ "$ONLY" = "all" ]
}
echo -e "${BLUE}π Running local CI checks for Soroban Smart Contracts${NC}"
echo "=================================================="
# Check if we're in the right directory
if [ ! -d "$PROJECT_DIR" ]; then
echo -e "${RED}β Error: $PROJECT_DIR directory not found${NC}"
echo "Make sure to run this script from the project root"
exit 1
fi
cd "$PROJECT_DIR"
# Track overall pass/fail across all checks
FAILED=0
# Function to run a command and report status
run_check() {
local name=$1
local cmd=$2
echo -e "\n${YELLOW}π $name${NC}"
echo "Running: $cmd"
if eval "$cmd"; then
echo -e "${GREEN}β
$name passed${NC}"
else
echo -e "${RED}β $name failed${NC}"
FAILED=1
fi
}
# βββ PREREQUISITES ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
echo -e "\n${BLUE}π Checking prerequisites...${NC}"
# Check Rust installation
if ! command -v rustc &> /dev/null; then
echo -e "${RED}β Rust not installed. Please install Rust first.${NC}"
exit 1
fi
# Install required Rust components
echo -e "\n${BLUE}π§ Installing Rust components...${NC}"
rustup component add rustfmt clippy
rustup target add wasm32-unknown-unknown
# βββ 1. CHECK (matches CI job: check) βββββββββββββββββββββββββββββββββββββββββ
if should_run check; then
echo -e "\n${BLUE}π§Ή [check] Format & Lint${NC}"
echo "================================================"
run_check "Format Check" "cargo fmt --all -- --check"
# --all-targets --all-features matches the CI clippy invocation, with the
# same -A allow flags for known false positives.
run_check "Clippy Linting" "cargo clippy --all-targets --all-features --workspace -- \
-D warnings \
-A deprecated \
-A dead_code \
-A unused-imports \
-A unused-attributes \
-A clippy::inconsistent-digit-grouping \
-A clippy::manual-range-contains \
-A clippy::unnecessary_cast"
fi
# βββ 2. SOROBAN-CHECKS (matches CI job: soroban-checks) βββββββββββββββββββββββ
if should_run soroban-checks; then
echo -e "\n${BLUE}π [soroban-checks] Soroban Validations${NC}"
echo "=========================================="
# Install Stellar CLI (used for build/optimize/inspect). Mirrors the CI job
# step which runs `cargo install --locked stellar-cli`.
if ! command -v stellar &> /dev/null; then
echo -e "${BLUE}π οΈ Installing Stellar CLI (cargo install --locked stellar-cli --version 25.2.0)...${NC}"
cargo install --locked stellar-cli --version 25.2.0
fi
run_check "Contract Build" "stellar contract build --verbose"
if [ -d "target/wasm32-unknown-unknown/release" ]; then
# Optimize each non-optimized wasm artifact, then inspect the result.
for wasm in target/wasm32-unknown-unknown/release/*.wasm; do
[ -f "$wasm" ] || continue
case "$wasm" in
*-optimized.wasm) continue ;;
esac
run_check "Contract Optimization" "stellar contract optimize --wasm $wasm"
optimized_wasm="${wasm%.wasm}-optimized.wasm"
if [ -f "$optimized_wasm" ]; then
run_check "Contract Inspection" "stellar contract inspect --wasm $optimized_wasm --output json"
fi
done
else
echo -e "${YELLOW}β οΈ No WASM files found to optimize/inspect${NC}"
fi
fi
# βββ 3. BUILD-AND-TEST (matches CI job: build-and-test on macos-latest) ββββββββ
if should_run build-and-test; then
echo -e "\n${BLUE}π§ͺ [build-and-test] Build & Tests${NC}"
echo "==================================="
run_check "Build" "cargo build --verbose"
run_check "Unit Tests" "cargo test --lib --verbose"
run_check "Integration Tests" "cargo test --tests --verbose"
run_check "Documentation Build" "cargo doc --no-deps --verbose"
fi
# βββ 4. AUDIT (matches CI job: audit) βββββββββββββββββββββββββββββββββββββββββ
if should_run audit; then
echo -e "\n${BLUE}π [audit] Security Audit${NC}"
echo "==========================="
if ! command -v cargo-audit &> /dev/null; then
echo -e "${BLUE}π οΈ Installing cargo-audit...${NC}"
# --locked here matches the workflow so the local install and the CI
# install resolve to the same advisory-db revision.
cargo install cargo-audit --version '^0.21' --locked
fi
# Ignored advisories live in stellar-lend/.cargo/audit.toml.
run_check "Security Audit" "cargo audit"
fi
# βββ 5. COVERAGE (matches CI job: coverage on ubuntu-latest) βββββββββββββββββββ
if should_run coverage; then
echo -e "\n${BLUE}π [coverage] Code Coverage${NC}"
echo "============================="
if ! command -v cargo-tarpaulin &> /dev/null; then
echo -e "${BLUE}π οΈ Installing cargo-tarpaulin...${NC}"
cargo install cargo-tarpaulin --locked --version 0.31.0
fi
echo -e "\n${YELLOW}π Generating coverage${NC}"
run_check "Coverage Generation" "cargo tarpaulin --verbose --out Xml --workspace"
# Coverage enforcement. cwd is stellar-lend/ (set at the top of this
# script), so cobertura.xml is in the cwd and the thresholds JSON lives at
# ../scripts/coverage_thresholds.json relative to that.
#
# We deliberately do NOT provide a "fall back to default 95%" branch here:
# any silent-swallow of a coverage failure (e.g. `|| true`) would let real
# below-threshold crates slip through unreported. If the JSON is missing
# for some reason, hard-fail immediately so the operator fixes the repo
# rather than mis-attributing the result.
echo -e "\n${YELLOW}π Enforcing per-crate thresholds${NC}"
if [ ! -f "../scripts/coverage_thresholds.json" ]; then
echo -e "${RED}β ../scripts/coverage_thresholds.json not found; cannot enforce coverage.${NC}"
exit 1
fi
run_check "Coverage Threshold Enforcement" "python3 ../scripts/enforce_coverage.py cobertura.xml --thresholds-json ../scripts/coverage_thresholds.json"
fi
# βββ SUMMARY ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
if [ "$FAILED" -eq 0 ]; then
echo -e "\n${GREEN}π All requested CI checks passed!${NC}"
else
echo -e "\n${RED}β Some CI checks failed.${NC}"
fi
echo "===================================="
echo -e "${YELLOW}Note: Some checks might behave slightly differently in CI (e.g. ephemeral cache state).${NC}"
echo -e "\n${BLUE}π‘ Quick fixes for common issues:${NC}"
echo "- Format issues: cd stellar-lend && cargo fmt --all"
echo "- Clippy warnings: cd stellar-lend && cargo clippy --fix --all-targets --all-features"
echo "- Build issues: check error output, fix code, re-run ./local-ci.sh"
echo "- Security issues: cargo update or add --ignore RUSTSEC-XXXX-XXXX to audit run"
echo "- Coverage: add tests, then re-run ./local-ci.sh --only coverage"
# Exit with non-zero if any check failed
exit "$FAILED"