Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
166 changes: 138 additions & 28 deletions scripts/foundry-patch.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env bash
#
# Patches a Foundry checkout to resolve tempo-* crates from a local Tempo
# checkout instead of git/crates-io. Used by both GitHub Actions (specs.yml)
# and the Argo invariant-tests workflow.
# checkout and to reuse Tempo's crates.io overrides for shared upstream crates.
# Used by both GitHub Actions (specs.yml) and the Argo invariant-tests workflow.
#
# Usage:
# scripts/foundry-patch.sh <tempo_root> <foundry_root>
Expand Down Expand Up @@ -36,6 +36,51 @@ if grep -q '^\[patch\."https://github.com/tempoxyz/tempo"\]' "$FOUNDRY_CARGO"; t
exit 0
fi

upsert_crates_io_patch() {
local crate="$1"
local replacement="$2"
local tmp_cargo

tmp_cargo="$(mktemp "${FOUNDRY_CARGO}.XXXXXX")"
awk -v crate="$crate" -v replacement="$replacement" '
/^\[patch\.crates-io\]/ {
seen = 1
in_section = 1
print
next
}
in_section && /^\[/ {
if (!done) {
print replacement
done = 1
}
in_section = 0
}
in_section {
line = $0
sub(/^[[:space:]]+/, "", line)
if (index(line, crate " = ") == 1) {
if (!done) {
print replacement
done = 1
}
next
}
}
{ print }
END {
if (!seen) {
print ""
print "[patch.crates-io]"
print replacement
} else if (in_section && !done) {
print replacement
}
}
' "$FOUNDRY_CARGO" > "$tmp_cargo"
mv "$tmp_cargo" "$FOUNDRY_CARGO"
}

# ── 1. Discover tempo-* workspace crates that have local paths ──────────────
PATCHES="$({
awk '
Expand Down Expand Up @@ -70,41 +115,50 @@ while IFS=$'\t' read -r crate path; do
[[ -n "$crate" ]] || continue
local_path="${TEMPO_ROOT}/${path}"
replacement="${crate} = { path = \"${local_path}\" }"
tmp_cargo="$(mktemp "${FOUNDRY_CARGO}.XXXXXX")"
awk -v crate="$crate" -v replacement="$replacement" '
upsert_crates_io_patch "$crate" "$replacement"
done <<< "$PATCHES"

# Tempo PRs sometimes patch shared upstream crates while Foundry still carries
# a stale lockfile entry for the registry version. Mirror those overrides into
# Foundry before resolving its lockfile so Forge builds against the same graph
# as the checked-out Tempo branch.
TEMPO_CRATES_IO_PATCHES="$(
awk '
/^\[patch\.crates-io\]/ {
seen = 1
in_section = 1
print
next
}
in_section && /^\[/ {
if (!done) {
print replacement
done = 1
}
in_section = 0
exit
}
in_section && index($0, crate " = ") == 1 {
if (!done) {
print replacement
done = 1
in_section {
line = $0
sub(/^[[:space:]]+/, "", line)
sub(/[[:space:]]+$/, "", line)
if (line == "" || line ~ /^#/) {
next
}
next
}
{ print }
END {
if (!seen) {
print ""
print "[patch.crates-io]"
print replacement
} else if (in_section && !done) {
print replacement
split(line, parts, "=")
crate = parts[1]
gsub(/[[:space:]]/, "", crate)
if (crate == "" || crate ~ /^tempo-/ || line !~ /^[A-Za-z0-9_-]+[[:space:]]*=/) {
next
}
print crate "\t" line
}
' "$FOUNDRY_CARGO" > "$tmp_cargo"
mv "$tmp_cargo" "$FOUNDRY_CARGO"
done <<< "$PATCHES"
' "$TEMPO_CARGO" | sort -u
)"

IMPORTED_CRATES_IO_PATCH_NAMES=()
if [[ -n "$TEMPO_CRATES_IO_PATCHES" ]]; then
echo "Importing Tempo [patch.crates-io] overrides into Foundry:"
while IFS=$'\t' read -r crate replacement; do
[[ -n "$crate" ]] || continue
echo "$replacement"
upsert_crates_io_patch "$crate" "$replacement"
IMPORTED_CRATES_IO_PATCH_NAMES+=("$crate")
done <<< "$TEMPO_CRATES_IO_PATCHES"
fi

echo "Updated Cargo.toml patch sections:"
sed -n '/^\[patch\./,$p' "$FOUNDRY_CARGO"
Expand Down Expand Up @@ -156,6 +210,61 @@ update_stale_tempo_git_packages() {
cargo update "${update_args[@]}" >/dev/null
}

update_imported_crates_io_patches() {
if ((${#IMPORTED_CRATES_IO_PATCH_NAMES[@]} == 0)); then
return 0
fi

local patch_pkgs=""
local crate
for crate in "${IMPORTED_CRATES_IO_PATCH_NAMES[@]}"; do
while IFS= read -r pkg; do
[[ -n "$pkg" ]] || continue
patch_pkgs+="${pkg}"$'\n'
done < <(
awk -v crate="$crate" '
/^\[\[package\]\]/ {
if (name == crate && version != "") {
print name "@" version
}
name = ""
version = ""
next
}
/^name = / {
name = $3
gsub(/"/, "", name)
next
}
/^version = / {
version = $3
gsub(/"/, "", version)
next
}
END {
if (name == crate && version != "") {
print name "@" version
}
}
' Cargo.lock
)
done

patch_pkgs="$(printf '%s' "$patch_pkgs" | sort -u)"
if [[ -z "$patch_pkgs" ]]; then
return 0
fi

local update_args=()
while IFS= read -r pkg; do
[[ -n "$pkg" ]] || continue
update_args+=("-p" "$pkg")
done <<< "$patch_pkgs"

echo "Cargo.lock contains Tempo [patch.crates-io] packages; running 'cargo update ${update_args[*]}'"
cargo update "${update_args[@]}" >/dev/null
}

# ── 4. Re-resolve the lockfile without upgrading unrelated crates ──────────
# `cargo update` can pull newer upstream deps from Foundry's workspace, which is non-deterministic.
# A normal resolver pass is enough to rewrite the lockfile entries for the tempo path overrides.
Expand All @@ -173,6 +282,7 @@ update_stale_tempo_git_packages() {
# conflicts twice in a row (i.e. `cargo update` made no progress).
pushd "$FOUNDRY_ROOT" >/dev/null
update_stale_tempo_git_packages
update_imported_crates_io_patches
prev_conflict_pkg=""
while true; do
err="$(cargo metadata --format-version=1 --no-default-features 2>&1 >/dev/null)" && break
Expand Down
Loading