Repository navigation
Expand file tree
/
Copy pathjustfile
More file actions
304 lines (277 loc) · 11.7 KB
/
Copy pathjustfile
File metadata and controls
304 lines (277 loc) · 11.7 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
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# Templar contract-mvp — project tasks. Run `just` to list recipes.
set positional-arguments
# `(^|::)` so the convention reaches tests nested in modules, not only those at
# an integration binary's root. A bare `^` anchors against the full path
# (`module::tests::requires_network_x`), so nested network tests silently ran in
# the fast gate.
network_filter := 'test(/(^|::)requires_network_/)'
sandbox_full_packages := trim('''
templar-market-contract
templar-vault-contract
templar-registry-contract
templar-patch-state-contract
templar-universal-account-contract
templar-proxy-oracle-near-contract
templar-proxy-oracle-near-governance-contract
templar-lst-oracle-contract
templar-funding-bridge
templar-relayer
templar-gateway-testing
templar-liquidator
templar-gateway-core
templar-manager
''')
sandbox_gateway_package := 'templar-gateway-service'
sandbox_packages := sandbox_full_packages + "\n" + sandbox_gateway_package
sandbox_package_filter := 'package(' + replace(sandbox_full_packages, "\n", ') | package(') + ')'
# `kind(test)` reaches integration targets only, so a node-backed test written as
# a unit test inside a lib or bin is routed by its name instead. Scoped to the
# sandbox packages: elsewhere the name would drop the test from both gates,
# since the node gate only lists these packages.
sandbox_name_filter := 'test(/(^|::)requires_sandbox_/)'
sandbox_unit_filter := '((' + sandbox_package_filter + ') & ' + sandbox_name_filter + ')'
sandbox_gateway_filter := 'package(' + sandbox_gateway_package + ') & (test(/^rpc::tests::/) | test(/^gateway_service::tests::/))'
sandbox_filter := '((kind(test) & (' + sandbox_package_filter + ')) | ' + sandbox_unit_filter + ' | (' + sandbox_gateway_filter + '))'
fast_filter := 'not ' + network_filter + ' and not ' + sandbox_filter
sandbox_test_filter := 'not ' + network_filter + ' and ' + sandbox_filter
sandbox_test_threads := '4'
artifacts_bin := 'cargo run --quiet -p templar-contract-artifacts --features fetch,clap --bin fetch-artifacts --'
# Show available recipes.
default:
@just --list
# Format SQL (standalone files + inline in Rust).
sql-fmt:
sleek $(find . -name '*.sql' -not -path './target/*')
./script/sql-fmt-inline.pl $(find . -name '*.rs' -not -path './target/*')
# Format Rust + SQL.
fmt: sql-fmt
cargo fmt
# Run the complete local suite with shared prerequisites established once. `--stale` reuses built Wasms.
test *args:
#!/usr/bin/env bash
set -euo pipefail
source ./script/postgres-up.sh
# --stale is the sandbox leg's alone; the fast leg forwards its args to nextest.
fast_args=()
for arg in "$@"; do [[ "$arg" == --stale ]] || fast_args+=("$arg"); done
just -- _test-fast "${fast_args[@]}"
just -- _test-sandbox "$@"
# Run the complete non-node gate.
test-fast *args:
#!/usr/bin/env bash
set -euo pipefail
source ./script/postgres-up.sh
just -- _test-fast "$@"
_test-fast *args:
#!/usr/bin/env bash
set -euo pipefail
# Include integration targets: this is the complete non-node partition.
cargo nextest run --ignore-default-filter \
-E '{{ fast_filter }}' "$@"
# Run the node-backed gate against a pooled neard sandbox. `--stale` reuses the Wasms in target/near.
test-sandbox *args:
#!/usr/bin/env bash
set -euo pipefail
source ./script/postgres-up.sh
just -- _test-sandbox "$@"
_test-sandbox *args:
#!/usr/bin/env bash
set -euo pipefail
trap './script/sandbox-down.sh || true' EXIT
nextest_args=()
sandbox_package_args=()
sandbox_test_threads='{{ sandbox_test_threads }}'
use_default_packages=true
while (($#)); do
case "$1" in
-p | -p?* | --package | --package=*)
use_default_packages=false
nextest_args+=("$1")
;;
--test-threads)
shift
if (($# == 0)); then
echo "error: --test-threads requires a value" >&2
exit 2
fi
sandbox_test_threads="$1"
;;
--test-threads=*)
sandbox_test_threads="${1#*=}"
;;
--stale)
export TEST_CONTRACTS_PREBUILT=1
;;
*)
nextest_args+=("$1")
;;
esac
shift
done
if [[ ! "$sandbox_test_threads" =~ ^[1-9][0-9]*$ ]]; then
echo "error: sandbox test threads must be a positive integer" >&2
exit 2
fi
if "$use_default_packages"; then
while IFS= read -r package; do
sandbox_package_args+=(-p "$package")
done <<< '{{ sandbox_packages }}'
# Build test binaries BEFORE starting the pool: nodes produce blocks from
# boot, so starting them first spends the whole cargo build competing for
# CPU (minutes of contention on CI). Default set only — a narrowed `-p`
# run keeps its package flags in nextest_args, which cargo build can't take.
cargo build --tests "${sandbox_package_args[@]}"
fi
SANDBOX_NODE_COUNT="$sandbox_test_threads" source ./script/sandbox-up.sh
cargo nextest run --profile sandbox --ignore-default-filter \
--test-threads "$sandbox_test_threads" \
"${sandbox_package_args[@]}" \
-E '{{ sandbox_test_filter }}' "${nextest_args[@]}"
# Start the out-of-band sandbox neard (prints its RPC url). `--stale` skips the Wasm prebuild.
sandbox-up *args:
#!/usr/bin/env bash
set -euo pipefail
for arg in "$@"; do
case "$arg" in
--stale)
export TEST_CONTRACTS_PREBUILT=1
;;
*)
echo "error: sandbox-up accepts only --stale" >&2
exit 2
;;
esac
done
SANDBOX_NODE_COUNT='{{ sandbox_test_threads }}' ./script/sandbox-up.sh
# Benchmark the sandbox harness primitives on a dedicated neard (never a pooled
# node) — block-latency floor, per-tx and per-patch costs, fixture setup.
bench-sandbox *args:
#!/usr/bin/env bash
set -euo pipefail
source ./script/prebuild-test-contracts.sh
# Debug profile, like the test gate itself: the numbers must be comparable
# with what tests actually pay, and the work being timed is node I/O.
cargo run -p templar-gateway-testing --bin sandbox-bench -- "$@"
# Stop the out-of-band sandbox neard.
sandbox-down:
./script/sandbox-down.sh
# Generate HTML coverage for the fast library-test cut.
coverage:
./script/coverage.sh html '{{ fast_filter }}'
# Generate LCOV coverage for CI.
coverage-lcov:
./script/coverage.sh lcov '{{ fast_filter }}'
# Regenerate the risk-parameter tables in docs/src.
docs-generate:
python3 script/docs/gen-risk-parameters.py
# Fail if a generated docs table is stale.
docs-check:
python3 script/docs/gen-risk-parameters.py --self-test
python3 script/docs/gen-risk-parameters.py --check
# Build the docs.
docs: docs-check
./script/build-docs.sh
# Warm the shared cache of released contract WASM.
#
# Downloads every release pinned in contract/artifacts/releases/ and verifies
# its SHA-256; migration and upgrade tests deploy these bytes. The cache is
# outside the repo (override with TEMPLAR_ARTIFACT_CACHE;
# TEMPLAR_ARTIFACT_OFFLINE=1 forbids downloads).
artifacts-fetch:
{{ artifacts_bin }}
# Print the resolved artifact cache directory.
artifacts-cache-path:
@{{ artifacts_bin }} --print-path
# Delete the artifact cache. Entries are immutable, so this only costs the next
# `just artifacts-fetch` a re-download.
artifacts-clean:
#!/usr/bin/env bash
set -euo pipefail
dir="$({{ artifacts_bin }} --print-path)"
# The crate resolves the path, so this cannot delete a directory it does not
# manage — but an empty expansion would make the `rm` unbounded.
[ -n "$dir" ] || { echo "could not resolve the artifact cache directory" >&2; exit 1; }
rm -rf -- "$dir"
echo "deleted $dir"
# Report whether a release tag's WASM is built, published and recorded.
release-wasm-status +tags:
#!/usr/bin/env bash
set -euo pipefail
# Captured, not piped into `grep -q`: under `pipefail` the early exit can
# SIGPIPE the producer and turn a match into a false negative.
recorded_tags=$(cut -f3 contract/artifacts/releases/*.tsv)
for tag in "$@"; do
if grep -Fxq "$tag" <<<"$recorded_tags"; then
recorded=yes
else
recorded=no
fi
assets=$(gh release view "$tag" --json assets \
--jq '.assets[].name | select(endswith(".wasm"))' 2>/dev/null || true)
if [ -n "$assets" ]; then
published=yes
else
published=no
fi
case "${recorded}/${published}" in
yes/yes) echo "${tag}: built and recorded" ;;
no/yes) echo "${tag}: published, not yet recorded — the catalog PR is pending" ;;
yes/no) echo "${tag}: recorded, but the release carries no WASM asset" ;;
no/no) echo "${tag}: not built" ;;
esac
done
# Build and publish a released version's canonical WASM, and queue its catalog row.
release-wasm +tags:
#!/usr/bin/env bash
set -euo pipefail
recorded_tags=$(cut -f3 contract/artifacts/releases/*.tsv)
pending=()
# Every tag is checked before any is dispatched, so a bad one late in the
# list cannot leave the earlier ones half-dispatched.
for tag in "$@"; do
git rev-parse -q --verify "refs/tags/${tag}" >/dev/null ||
{ echo "no such tag: ${tag} (try 'git fetch --tags')" >&2; exit 1; }
if grep -Fxq "$tag" <<<"$recorded_tags"; then
echo "${tag}: already recorded; nothing to build"
continue
fi
# Answered by the local worktree rather than the tag — so a package
# renamed since the tag reads as uncatalogued here even though CI,
# checking the tag out, would resolve it.
status=0
resolved=$(cargo run --quiet -p templar-contract-artifacts \
--features workspace-loader,clap --bin prebuild-test-contracts -- \
--resolve "$tag" 2>&1) || status=$?
if [ "$status" -eq 2 ]; then
echo "${tag}: names no catalogued WASM artifact" >&2
exit 1
elif [ "$status" -ne 0 ]; then
printf '%s\n' "$resolved" >&2
exit "$status"
fi
# The other half of that asymmetry: the workflow resolves the tag from
# the tag's own checkout, where a Soroban tag cut before the catalog
# landed (#688) names nothing and skips green with no WASM built. Only
# this side knows the tag IS catalogued, so only this side can tell a
# pre-catalog tag from an uncatalogued one. Here is where it stops, and
# RELEASING.md already routes such tags to building at the tag.
if grep -Fxq kind=soroban <<<"$resolved"; then
# Captured, not piped into `grep -q`: under `pipefail` the early
# exit turns a present tag into a missing one.
tag_ids=$(git show "${tag}:contract/artifacts/src/ids.rs" 2>/dev/null || true)
if ! grep -qF SorobanArtifactId <<<"$tag_ids"; then
echo "${tag}: predates the Soroban catalog (#688); its own checkout" \
"resolves no artifact for it." >&2
echo "${tag}: check the tag out, run its 'just optimize-<slug>', and" \
"record it by hand — see 'Contract WASM artifacts' in RELEASING.md." >&2
exit 1
fi
fi
pending+=("$tag")
done
# Dispatched against the default branch, never the tag: GitHub runs the
# workflow file as it exists at the ref it is dispatched against.
for tag in "${pending[@]+"${pending[@]}"}"; do
gh workflow run release-artifacts.yml --ref dev -f tag="$tag"
echo "${tag}: dispatched"
done