diff --git a/.agents/skills/upgrade-snarkvm/SKILL.md b/.agents/skills/upgrade-snarkvm/SKILL.md new file mode 100644 index 00000000..3132e7bb --- /dev/null +++ b/.agents/skills/upgrade-snarkvm/SKILL.md @@ -0,0 +1,258 @@ +--- +name: upgrade-snarkvm +description: Use when upgrading the SDK's pinned upstream Rust dependencies — snarkVM (sdk/) and leo (sdk-abi/) — to their latest releases. Compares tags, adapts the code to the changeset, runs the native validation loop, bumps PyPI package versions, and opens a PR. Invoked as /upgrade-snarkvm interactively or by the snarkvm-upgrade workflow. +--- + +# Upgrade snarkVM and leo + +Upgrade the SDK's two upstream Rust pins to their latest releases and adapt +the code. Same procedure interactively and in CI. + +Two independent pins, each with its own release cadence: + +- **`sdk/`** pins `snarkvm` — tracks snarkVM's latest mainnet release tag. +- **`sdk-abi/`** pins four `leo` crates by rev (`leo-abi`, `leo-ast`, + `leo-disassembler`, `leo-span`) plus a `snarkvm` dep that must match leo's + — tracks leo's latest release. + +Either can be behind independently, so a run may upgrade one, the other, or +both. The pre-check tells you which. + +## Hard rules + +- Plain commit messages — NO Co-Authored-By trailers, NO "Generated with" + footers. +- Every version number comes from a command output. Never guess one. +- Dependency form follows one policy: prefer the crates.io release when its + version equals the latest GitHub tag; otherwise pin the git tag. The + pre-check's `crates_io` output already answers this — don't re-derive it. +- `sdk-abi` does NOT get to apply that policy. Its snarkvm dep must resolve + to the **same source *and* version as leo's**, because cargo compiles one + crate from two different sources twice: registry `4.8.1` and + `tag = "v4.8.1"` are distinct crates, so `Process`/`Program` in + leo-disassembler's signatures stop unifying with sdk-abi's. Mirror leo's + snarkvm dep line verbatim — same source, same version/tag, same features — + and never choose it independently. This means **sdk-abi's snarkvm can and + will lag `sdk/`'s** whenever leo hasn't adopted the newest snarkVM; that is + correct and expected, not a problem to fix. The two are separate build + graphs (`sdk-abi/Cargo.toml` declares its own `[workspace]`), so skew + between them is harmless. +- Proof the source identity held: after building sdk-abi, + `grep -c '^name = "snarkvm"' sdk-abi/Cargo.lock` must be exactly **1**. A + 2 means leo and sdk-abi disagree on the source and the type errors you're + about to see are that, not a real API break. +- Validate seriously before giving up: on failures, re-read the changeset, + fix, and re-run. Only after several genuine fix attempts do you fall back + to the draft-PR path in step 8. + +## 1. Detect + +```bash +bash .github/scripts/snarkvm_check.sh +``` + +If `needs_upgrade=false`, report why (both pins current, or an open PR exists +for `branch` — check `gh pr list --head ""`) and STOP. + +Otherwise read the outputs into: + +- `snarkvm_needs_upgrade` — do step 3. CUR=`current`, NEW=`latest`, + CRATES_IO=`crates_io`. +- `leo_needs_upgrade` — do step 4. LEO_CUR=`leo_current`, + LEO_NEW=`leo_latest`, LEO_CUR_REV=`leo_current_rev`, + LEO_REV=`leo_latest_rev`. +- `branch` — the branch to push in step 8. Use it verbatim; it already + encodes which pins moved. + +Skip whichever step's flag is false, and skip that package's edits entirely — +a leo-only run must leave `sdk/` untouched, and vice versa. + +Also note `devnode_latest`/`devnode_snarkvm`: which snarkvm the newest +aleo-devnode release pins. Purely informational — the SDK carries no devnode +pin (the binary comes from PATH and `-m devnode` tests skip in CI) — but the +PR body must say whether the devnode matches $NEW (step 8). The devnode often +tracks `testnet-v*` tags, so a mismatch there is normal. + +## 2. Study the snarkVM changeset (only if snarkvm_needs_upgrade=true) + +Use an existing local snarkVM clone if one is available (fetch its tags +first); otherwise clone to a scratch dir: + +```bash +git clone --filter=blob:none https://github.com/ProvableHQ/snarkVM /tmp/snarkvm-upgrade-src +cd /tmp/snarkvm-upgrade-src +git log --oneline $CUR..$NEW +git diff --stat $CUR..$NEW -- console/ circuit/ synthesizer/ ledger/ algorithms/ utilities/ parameters/ +``` + +Diff in detail the areas the SDK binds (what `sdk/src/*.rs` and +`sdk-abi/src/*.rs` `use`): console types (Address, PrivateKey, ViewKey, +Signature, Plaintext, Record, Literal), ledger (Block, Transaction, +Transition, queries), synthesizer (Process, Program, Authorization, +execution/deployment), and anything ARC-20/ARC-22-relevant. Write a short +breaking-change inventory (API renames, signature changes, semantic changes) +BEFORE editing anything. + +## 3. Bump the snarkvm pin in sdk/ (only if snarkvm_needs_upgrade=true) + +In `sdk/Cargo.toml`, replace the `snarkvm = {` line. CRATES_IO=true means +crates.io publishes a release whose version equals $NEW; false means the tag +is ahead of the registry, so the git tag is the only way to get $NEW. + +- If CRATES_IO=true — switch to the crates.io form, keeping the feature list + verbatim: + ```toml + snarkvm = { version = "X.Y.Z", default-features = false, features = [ + "console", "circuit", "synthesizer", "ledger", "utilities", "algorithms", "parameters", + ] } + ``` +- If CRATES_IO=false — keep the git form, only changing the tag: + ```toml + snarkvm = { git = "https://github.com/ProvableHQ/snarkVM.git", tag = "vX.Y.Z", default-features = false, features = [ ... ] } + ``` + +`sdk/Cargo.lock` refreshes on the next build — commit it with the change. + +## 4. Bump leo in sdk-abi/ (only if leo_needs_upgrade=true) + +sdk-abi tracks leo's releases. Leo tags per crate +(`leo-abi-vX.Y.Z`, `leo-lang-vX.Y.Z`, …) and one release tags several crates +at a single rev — but not every crate every time, so don't trust any single +crate's tag list. The pre-check already resolved the newest release for you: +LEO_NEW is the version, LEO_REV the rev its tags point at. + +Study what changed first — sdk-abi binds leo's ABI surface, which moves more +than snarkVM's: + +```bash +git clone --filter=blob:none https://github.com/ProvableHQ/leo /tmp/leo-upgrade-src +cd /tmp/leo-upgrade-src +git log --oneline $LEO_CUR_REV..$LEO_REV -- crates/abi/ crates/disassembler/ crates/ast/ +git diff $LEO_CUR_REV..$LEO_REV -- crates/abi/src crates/disassembler/src +``` + +Then in `sdk-abi/Cargo.toml`: + +1. Update EVERY leo crate's `rev = "..."` to LEO_REV — all four must move + together (`leo-abi`, `leo-ast`, `leo-disassembler`, `leo-span`); a partial + bump gives you two leo versions in one graph and unresolvable type errors. +2. Read leo's snarkvm dep line **at LEO_REV** and mirror it verbatim: + +```bash +curl -fsS "https://raw.githubusercontent.com/ProvableHQ/leo/$LEO_REV/Cargo.toml" | grep -m1 '^snarkvm ' +``` + +Copy its source form, version/tag, and feature list into sdk-abi's `snarkvm` +line exactly — see the hard rule above on why this is not a choice. If that +version differs from `sdk/`'s, say so in the PR body; the skew is expected. + +## 5. Adapt the code + +Fix `sdk/src/` (if step 3 ran) and `sdk-abi/src/` (if step 4 ran), guided by +the step-2 and step-4 inventories. Start with `cargo check` in each affected +package; compile errors first, then semantics. For sdk-abi, a wall of +`Process`/`Program` mismatches usually means the snarkvm source didn't +match leo's — re-check that before touching any code. + +## 6. Validate (native loop, escalating — all from `sdk/`) + +Run the whole loop regardless of which pin moved: sdk-abi ships an import +hook into the main package (`sdk/python/tests/test_abi_hook.py`), so a +leo-only upgrade can still break `sdk/`'s tests. The cost is cache-warm +rebuilds, not correctness risk. + +Interactively, work inside `sdk/.env` (`source .env/bin/activate`); in CI use +the runner's python directly. + +```bash +cd sdk +cargo fmt --check +cargo clippy --no-default-features --features mainnet -- -D warnings +cargo check --no-default-features --features testnet + +pip install maturin +maturin build --release --features mainnet --out dist +sed -i.bak 's/module-name = "aleo._aleolib_mainnet"/module-name = "aleo._aleolib_testnet"/' pyproject.toml +maturin build --release --no-default-features --features testnet --out dist-testnet +mv pyproject.toml.bak pyproject.toml +python ../.github/scripts/merge_testnet_so.py dist dist-testnet +pip install --force-reinstall dist/aleo_sdk-*.whl +pip install pytest pytest-xdist httpx pynacl responses pytest-asyncio requests + +python -m pytest python/tests -v -n auto -m "not slow and not live and not devnode" +python -m pytest python/tests -v -m slow +python -m pytest python/tests/test_testnet.py -v +``` + +Then the abi package (run these even if sdk-abi was held back — they prove +the version skew is harmless): + +```bash +cd .. +maturin build --release --manifest-path sdk-abi/Cargo.toml --out dist +pip install --force-reinstall dist/aleo_contract_abi_generator-*.whl +( cd sdk-abi && python -m pytest python/tests -v ) +( cd sdk && python -m pytest python/tests/test_abi_hook.py -v ) +# Build-graph isolation canary — leo feature flags must not leak into sdk: +test "$(grep -c dev_skip_checks sdk/Cargo.lock || true)" = "0" +# Source-identity canary — one snarkvm copy, or leo's types won't unify: +test "$(grep -c '^name = "snarkvm"' sdk-abi/Cargo.lock || true)" = "1" +``` + +On any failure: diagnose against the changeset inventory, fix, re-run the +failing stage, then re-run the full loop once everything passes. + +## 7. Bump PyPI package versions + +For each of `aleo-sdk`, `aleo-contract-abi-generator`, `shield-swap-sdk`: + +```bash +curl -fsS https://pypi.org/pypi//json | python3 -c "import json,sys; print(json.load(sys.stdin)['info']['version'])" +``` + +New version = live version with patch incremented (e.g. 0.3.0 → 0.3.1). If +any query fails, STOP the version-bump step and say so — never guess. +Apply to: +- `sdk/pyproject.toml` + `sdk/Cargo.toml` `[package] version` (aleo-sdk) +- `sdk-abi/pyproject.toml` + `sdk-abi/Cargo.toml` `[package] version` +- `shield-swap-sdk/pyproject.toml` + +Rebuild lockfiles by running `cargo check` in `sdk/` and `sdk-abi/`; commit +lockfile changes. + +## 8. PR + +Use `branch` from step 1 verbatim — it already names what moved +(`deps-upgrade/snarkvm-v4.9.0`, `deps-upgrade/leo-v4.3.4`, or both joined). +Stage explicit paths, never `git add -A`: the validation loop leaves build +artifacts (`dist/`, `sdk/dist*/`) that are not gitignored. + +```bash +git checkout -B "$branch" # -B resets a stale branch with no open PR +git add sdk/Cargo.toml sdk/Cargo.lock sdk/pyproject.toml \ + sdk-abi/Cargo.toml sdk-abi/Cargo.lock sdk-abi/pyproject.toml \ + shield-swap-sdk/pyproject.toml +git add -u sdk/src sdk-abi/src # any code adaptations from step 5 +git commit -m "" # e.g. "deps: upgrade snarkvm to v4.9.0, leo to v4.3.4" +git push -u origin "$branch" --force-with-lease +``` + +Title names only the pins that actually moved. Body must cover: + +- the breaking-change inventory per upgraded dep, and what was adapted +- `sdk/` dep-form decision (crates.io vs git tag, and why) +- `sdk-abi/`: leo rev old→new, and the snarkvm line mirrored from leo — + state explicitly when it differs from `sdk/`'s, e.g. "sdk-abi stays on + snarkvm 4.8.1 because leo v4.3.4 pins it; sdk/ is on 4.9.0. Expected — + separate build graphs." +- validation evidence: which suites ran, pass counts, and both canaries +- the PyPI version bumps +- devnode: "aleo-devnode <devnode_latest> pins snarkvm <devnode_snarkvm> — + matches $NEW" or "— differs from $NEW: local `-m devnode` e2e tests may + skip on fee/consensus skew until a devnode release adopts it" (the devnode + frequently tracks `testnet-v*`, so a mismatch is usually not actionable) + +Then `gh pr create --title "<title>" --body ...`. +Genuinely stuck after repeated fix attempts → same, but `gh pr create +--draft`, with the body additionally listing exact failing tests, output +excerpts, and what was attempted. diff --git a/.github/scripts/snarkvm_check.sh b/.github/scripts/snarkvm_check.sh new file mode 100755 index 00000000..382f1786 --- /dev/null +++ b/.github/scripts/snarkvm_check.sh @@ -0,0 +1,191 @@ +#!/usr/bin/env bash +# Are the SDK's two upstream pins — snarkvm (sdk/) and leo (sdk-abi/) — behind +# their latest releases, and is an upgrade PR already open? +# +# Output (stdout `key=value`; also appended to $GITHUB_OUTPUT when set): +# current=v4.8.1 sdk/'s pinned snarkvm version +# latest=v4.9.0 newest bare vX.Y.Z snarkvm tag (no testnet-/canary-) +# crates_io=true|false does crates.io publish a release equal to `latest`? +# snarkvm_needs_upgrade=true|false +# leo_current=4.3.2 leo version at sdk-abi's pinned rev +# leo_latest=4.3.4 newest leo release version (max over leo-*-vX.Y.Z tags) +# leo_current_rev=<sha> sdk-abi's pinned leo rev +# leo_latest_rev=<sha> rev the newest leo release tags point at +# leo_needs_upgrade=true|false +# branch=deps-upgrade/... branch the upgrade should use (empty if none needed) +# needs_upgrade=true|false either pin behind, and no PR open for `branch` +# devnode_latest=v0.2.2 newest aleo-devnode release (informational — +# devnode_snarkvm=testnet-v4.9.0 the SDK carries no devnode pin; this tells +# the upgrade PR whether the devnode will skew) +# +# Usage: snarkvm_check.sh [--current vX.Y.Z] [--leo-current X.Y.Z] +# Overrides pin detection (testing / workflow force_from_tag). +set -euo pipefail + +REPO_URL="https://github.com/ProvableHQ/snarkVM" +LEO_URL="https://github.com/ProvableHQ/leo" +DEVNODE_URL="https://github.com/ProvableHQ/aleo-devnode" +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +CARGO_TOML="$ROOT/sdk/Cargo.toml" +ABI_CARGO="$ROOT/sdk-abi/Cargo.toml" + +force_current="" +force_leo="" +while [[ $# -gt 0 ]]; do + case "$1" in + --current) force_current="${2:?--current requires a tag argument}"; shift 2 ;; + --leo-current) force_leo="${2:?--leo-current requires a version argument}"; shift 2 ;; + *) echo "error: unknown argument '$1'" >&2; exit 1 ;; + esac +done + +# ---------------------------------------------------------------- snarkvm ---- +if [[ -n "$force_current" ]]; then + current="$force_current" +else + # Fail-soft the grep so the parse error below reports the real problem + # rather than set -e killing the script with a bare exit 1. + dep_line=$(grep -m1 '^snarkvm = {' "$CARGO_TOML" || true) + if [[ $dep_line =~ tag\ =\ \"(v[0-9]+\.[0-9]+\.[0-9]+)\" ]]; then + current="${BASH_REMATCH[1]}" + elif [[ $dep_line =~ version\ =\ \"([0-9]+\.[0-9]+\.[0-9]+)\" ]]; then + current="v${BASH_REMATCH[1]}" + else + echo "error: could not parse snarkvm pin from $CARGO_TOML" >&2 + exit 1 + fi +fi + +latest=$(git ls-remote --tags "$REPO_URL" \ + | awk '{print $2}' \ + | sed 's|^refs/tags/||; s|\^{}$||' \ + | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' \ + | sort -uV | tail -1 || true) +if [[ -z "$latest" ]]; then + echo "error: no vX.Y.Z release tags found on $REPO_URL" >&2 + exit 1 +fi + +# Prefer the crates.io release when it matches `latest`; otherwise the tag is +# ahead of the registry and only the git pin can reach it. +crates_io=false +if curl -fsS -o /dev/null -A "ProvableHQ/python-sdk snarkvm-upgrade check" \ + "https://crates.io/api/v1/crates/snarkvm/${latest#v}"; then + crates_io=true +fi + +snarkvm_needs_upgrade=false +[[ "$current" != "$latest" ]] && snarkvm_needs_upgrade=true + +# -------------------------------------------------------------------- leo ---- +# sdk-abi pins leo by rev. Leo tags per crate (leo-abi-v4.3.4, leo-lang-v4.3.4, +# ...) and one release tags several crates at a single rev — but not every +# crate every time (leo-abi's newest tag lags leo-lang's). So take the highest +# version across ALL leo-*-v tags as the latest release, then resolve any tag +# at that version to its rev. +leo_current_rev="$(grep -m1 -oE 'rev = "[0-9a-f]{7,40}"' "$ABI_CARGO" \ + | grep -oE '[0-9a-f]{7,40}' || true)" + +leo_tags=$(git ls-remote --tags "$LEO_URL" 2>/dev/null \ + | awk '{print $2}' \ + | sed 's|^refs/tags/||; s|\^{}$||' \ + | grep -E '^leo-[a-z0-9-]+-v[0-9]+\.[0-9]+\.[0-9]+$' | sort -u || true) +leo_latest=$(printf '%s\n' "$leo_tags" | sed -E 's/^leo-[a-z0-9-]+-v//' \ + | sort -uV | tail -1 || true) + +leo_latest_rev="" +if [[ -n "$leo_latest" ]]; then + leo_latest_tag=$(printf '%s\n' "$leo_tags" \ + | grep -E -- "-v${leo_latest//./\\.}\$" | head -1 || true) + if [[ -n "${leo_latest_tag:-}" ]]; then + leo_latest_rev=$(git ls-remote "$LEO_URL" "refs/tags/$leo_latest_tag" \ + 2>/dev/null | awk '{print $1}' | head -1 || true) + fi +fi + +# Which leo version is the pinned rev? Read it from leo's own manifest at that +# rev — no extra bookkeeping in sdk-abi to drift out of date. +if [[ -n "$force_leo" ]]; then + leo_current="$force_leo" +elif [[ -n "$leo_current_rev" ]]; then + leo_current=$(curl -fsS \ + "https://raw.githubusercontent.com/ProvableHQ/leo/${leo_current_rev}/Cargo.toml" \ + 2>/dev/null | grep -m1 '^leo-abi ' \ + | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || true) + leo_current="${leo_current:-unknown}" +else + leo_current=unknown +fi + +# Only claim an upgrade when both sides are known — an unknown must never look +# like "behind" and kick off a 90-minute run on bad data. +leo_needs_upgrade=false +if [[ "$leo_current" != unknown && -n "$leo_latest" && -n "$leo_latest_rev" \ + && "$leo_current" != "$leo_latest" ]]; then + leo_needs_upgrade=true +fi + +# ----------------------------------------------------------------- branch ---- +slug="" +[[ "$snarkvm_needs_upgrade" == true ]] && slug="snarkvm-$latest" +if [[ "$leo_needs_upgrade" == true ]]; then + slug="${slug:+$slug-}leo-v$leo_latest" +fi +branch="${slug:+deps-upgrade/$slug}" + +# If a PR for this exact branch is already open, don't re-attempt daily. +# Fail-soft when gh is unavailable/unauthenticated: worst case the skill's +# own `gh pr create` collides on the existing head branch and errors there. +pr_open=0 +if [[ -n "$branch" ]] && command -v gh >/dev/null 2>&1; then + pr_open=$(gh pr list --head "$branch" --state open \ + --json number --jq length 2>/dev/null || echo 0) +fi + +needs_upgrade=false +if [[ -n "$branch" && "$pr_open" == "0" ]]; then + needs_upgrade=true +fi + +# ---------------------------------------------------------------- devnode ---- +# Informational only — never gates the upgrade. Which snarkvm does the latest +# devnode release pin? Handles both git-tag and crates.io dep forms; fail-soft +# to "unknown" on network/parse trouble. +devnode_latest=$(git ls-remote --tags "$DEVNODE_URL" 2>/dev/null \ + | awk '{print $2}' \ + | sed 's|^refs/tags/||; s|\^{}$||' \ + | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' \ + | sort -uV | tail -1 || true) +devnode_snarkvm=unknown +if [[ -n "$devnode_latest" ]]; then + devnode_dep=$(curl -fsS \ + "https://raw.githubusercontent.com/ProvableHQ/aleo-devnode/${devnode_latest}/Cargo.toml" \ + 2>/dev/null | grep -m1 '^snarkvm ' || true) + # The devnode tracks testnet tags as often as mainnet ones, so keep any + # testnet-/canary- prefix verbatim — "testnet-v4.9.0" != "v4.9.0" and the + # PR body should say which line the devnode is on. + if [[ $devnode_dep =~ tag\ =\ \"((testnet-|canary-)?v[0-9]+\.[0-9]+\.[0-9]+)\" ]]; then + devnode_snarkvm="${BASH_REMATCH[1]}" + elif [[ $devnode_dep =~ version\ =\ \"([0-9]+\.[0-9]+\.[0-9]+)\" ]]; then + devnode_snarkvm="v${BASH_REMATCH[1]}" + fi +fi +devnode_latest="${devnode_latest:-unknown}" + +out="current=$current +latest=$latest +crates_io=$crates_io +snarkvm_needs_upgrade=$snarkvm_needs_upgrade +leo_current=$leo_current +leo_latest=${leo_latest:-unknown} +leo_current_rev=${leo_current_rev:-unknown} +leo_latest_rev=${leo_latest_rev:-unknown} +leo_needs_upgrade=$leo_needs_upgrade +branch=$branch +needs_upgrade=$needs_upgrade +devnode_latest=$devnode_latest +devnode_snarkvm=$devnode_snarkvm" +echo "$out" +if [[ -n "${GITHUB_OUTPUT:-}" ]]; then + echo "$out" >> "$GITHUB_OUTPUT" +fi diff --git a/.github/workflows/snarkvm-upgrade.yml b/.github/workflows/snarkvm-upgrade.yml new file mode 100644 index 00000000..e143ed4f --- /dev/null +++ b/.github/workflows/snarkvm-upgrade.yml @@ -0,0 +1,107 @@ +# Daily upstream-dependency check for snarkvm (sdk/) and leo (sdk-abi/). When +# either is behind its latest release, a Claude session runs the +# /upgrade-snarkvm skill (.agents/skills/upgrade-snarkvm/SKILL.md) to bump, +# adapt, validate, and open a PR. +# +# One-time setup (repo secrets): +# ANTHROPIC_API_KEY — for claude-code-action +# SNARKVM_UPGRADE_PAT — classic PAT or app token with repo scope; used for +# checkout/push/gh so the created PR triggers +# on:pull_request CI (default GITHUB_TOKEN PRs don't) +name: snarkVM Upgrade + +on: + workflow_dispatch: + inputs: + force_from_tag: + description: "Pretend sdk/ pins this snarkvm tag (testing, e.g. v4.8.0)" + required: false + default: "" + force_leo_from_version: + description: "Pretend sdk-abi/ pins this leo version (testing, e.g. 4.3.2)" + required: false + default: "" + schedule: + - cron: "0 15 * * *" # 08:00 PDT (07:00 PST — GitHub cron is UTC-only) + +# One upgrade attempt at a time: a manual dispatch overlapping the cron would +# otherwise put two agents on the same snarkvm-upgrade/<tag> branch. Queue +# rather than cancel — a mid-flight run has real work in progress. +concurrency: + group: snarkvm-upgrade + cancel-in-progress: false + +permissions: + contents: write + pull-requests: write + +jobs: + check: + runs-on: ubuntu-latest + outputs: + current: ${{ steps.check.outputs.current }} + latest: ${{ steps.check.outputs.latest }} + leo_current: ${{ steps.check.outputs.leo_current }} + leo_latest: ${{ steps.check.outputs.leo_latest }} + snarkvm_needs_upgrade: ${{ steps.check.outputs.snarkvm_needs_upgrade }} + leo_needs_upgrade: ${{ steps.check.outputs.leo_needs_upgrade }} + branch: ${{ steps.check.outputs.branch }} + needs_upgrade: ${{ steps.check.outputs.needs_upgrade }} + steps: + - uses: actions/checkout@v4 + - id: check + # Dispatch inputs come through env, not ${{ }} interpolation into the + # script body, so a crafted value is data rather than shell syntax. + env: + GH_TOKEN: ${{ github.token }} + FORCE_TAG: ${{ inputs.force_from_tag }} + FORCE_LEO: ${{ inputs.force_leo_from_version }} + run: | + args=() + if [ -n "$FORCE_TAG" ]; then + [[ $FORCE_TAG =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] \ + || { echo "::error::force_from_tag must look like v4.8.0"; exit 1; } + args+=(--current "$FORCE_TAG") + fi + if [ -n "$FORCE_LEO" ]; then + [[ $FORCE_LEO =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] \ + || { echo "::error::force_leo_from_version must look like 4.3.2"; exit 1; } + args+=(--leo-current "$FORCE_LEO") + fi + bash .github/scripts/snarkvm_check.sh "${args[@]}" + + upgrade: + needs: check + if: needs.check.outputs.needs_upgrade == 'true' + runs-on: ubuntu-latest + timeout-minutes: 90 + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.SNARKVM_UPGRADE_PAT }} + fetch-depth: 0 + - uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + - uses: Swatinem/rust-cache@v2 + with: + workspaces: | + sdk + sdk-abi + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + # SRS/proving parameters — same cache key as sdk.yml so it's warm. + - uses: actions/cache@v4 + with: + path: ~/.aleo + key: aleo-parameters-${{ runner.os }}-v1 + - uses: anthropics/claude-code-action@v1 + env: + GH_TOKEN: ${{ secrets.SNARKVM_UPGRADE_PAT }} + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + github_token: ${{ secrets.SNARKVM_UPGRADE_PAT }} + prompt: "/upgrade-snarkvm — snarkvm ${{ needs.check.outputs.current }} -> ${{ needs.check.outputs.latest }} (needed: ${{ needs.check.outputs.snarkvm_needs_upgrade }}); leo ${{ needs.check.outputs.leo_current }} -> ${{ needs.check.outputs.leo_latest }} (needed: ${{ needs.check.outputs.leo_needs_upgrade }}); branch ${{ needs.check.outputs.branch }}. Re-run the pre-check yourself for the full outputs. You are in CI: work non-interactively to completion." + claude_args: | + --allowedTools "Bash,Read,Edit,Write,Glob,Grep"