From 7ffdc632d44ff5b77ddbabc68158c9feff815bd4 Mon Sep 17 00:00:00 2001 From: Han Date: Fri, 31 Oct 2025 17:48:59 +0800 Subject: [PATCH] Auto zkvm update check (#186) --- .github/scripts/fetch-zkvm-version.sh | 58 +++++++++ .github/workflows/check-zkvm-version.yml | 131 ++++++++++++++++++++ .github/workflows/test-zkvm.yml | 2 +- Cargo.lock | 2 - crates/build-utils/Cargo.toml | 2 - crates/build-utils/src/docker.rs | 145 ----------------------- crates/build-utils/src/lib.rs | 2 - 7 files changed, 190 insertions(+), 152 deletions(-) create mode 100755 .github/scripts/fetch-zkvm-version.sh create mode 100644 .github/workflows/check-zkvm-version.yml delete mode 100644 crates/build-utils/src/docker.rs diff --git a/.github/scripts/fetch-zkvm-version.sh b/.github/scripts/fetch-zkvm-version.sh new file mode 100755 index 0000000..9141e70 --- /dev/null +++ b/.github/scripts/fetch-zkvm-version.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +set -e -o pipefail + +# Usage: ./fetch-zkvm-version.sh +# Examples: +# .github/scripts/fetch-zkvm-version.sh airbender execution_utils +# .github/scripts/fetch-zkvm-version.sh jolt jolt-sdk +# .github/scripts/fetch-zkvm-version.sh miden miden-core +# .github/scripts/fetch-zkvm-version.sh nexus nexus-sdk +# .github/scripts/fetch-zkvm-version.sh openvm openvm-sdk +# .github/scripts/fetch-zkvm-version.sh pico pico-vm +# .github/scripts/fetch-zkvm-version.sh risc0 risc0-zkvm +# .github/scripts/fetch-zkvm-version.sh sp1 sp1-sdk +# .github/scripts/fetch-zkvm-version.sh ziren zkm-sdk +# .github/scripts/fetch-zkvm-version.sh zisk 0xPolygonHermez/zisk + +if [ $# -ne 2 ]; then + echo "Usage: $0 " + echo " crate: crate (e.g. openvm-sdk) or github org/repo (e.g. 0xPolygonHermez/zisk)" + exit 1 +fi + +ZKVM=$1 +CRATE=$2 + +get_github_latest() { + local org_repo=$1 + curl -sL "https://api.github.com/repos/$org_repo/tags" | grep -oP '"name":\s*"\K[^"]+' | head -1 +} + +get_crates_io_latest() { + local crate=$1 + curl -sL -A "EreCI" "https://crates.io/api/v1/crates/$crate" | grep -oP '"max_version":"\K[^"]+' +} + +if [[ "$CRATE" == */* ]]; then + # It is in format of org/repo, get current version from build.rs + LATEST=$(get_github_latest "$CRATE") + CURRENT=$(grep -oP 'gen_name_and_sdk_version\("'"$ZKVM"'", "\K[^"]+' "crates/zkvm/$ZKVM/build.rs") +else + # It is a crate name, get current version from Cargo.toml + LINE=$(grep "$CRATE" Cargo.toml) + + if echo "$LINE" | grep -q "git ="; then + # Dependency from github.com + REPO=$(echo "$LINE" | grep -oP 'git = "https://github.com/\K[^"]+' | sed 's/\.git$//') + CURRENT=$(echo "$LINE" | grep -oP 'tag = "\K[^"]+') + LATEST=$(get_github_latest "$REPO") + else + # Dependency from crates.io + CURRENT=$(grep "^$CRATE = " Cargo.toml | grep -oP '"\K[0-9.]+(?=")') + LATEST=$(get_crates_io_latest "$CRATE") + fi +fi + +echo "CURRENT=v${CURRENT#v}" +echo "LATEST=v${LATEST#v}" diff --git a/.github/workflows/check-zkvm-version.yml b/.github/workflows/check-zkvm-version.yml new file mode 100644 index 0000000..00ef4de --- /dev/null +++ b/.github/workflows/check-zkvm-version.yml @@ -0,0 +1,131 @@ +name: Check zkVM version + +on: + schedule: + # Run every day at 00:00 UTC + - cron: '0 0 * * *' + workflow_dispatch: + +permissions: + issues: write + contents: read + +jobs: + check-zkvm-version: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - zkvm: airbender + crate: execution_utils + - zkvm: jolt + crate: jolt-sdk + - zkvm: miden + crate: miden-core + - zkvm: nexus + crate: nexus-sdk + - zkvm: openvm + crate: openvm-sdk + - zkvm: pico + crate: pico-vm + - zkvm: risc0 + crate: risc0-zkvm + - zkvm: sp1 + crate: sp1-sdk + - zkvm: ziren + crate: zkm-sdk + - zkvm: zisk + crate: 0xPolygonHermez/zisk + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Check ${{ matrix.zkvm }} version + id: check + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + set -e -o pipefail + + RESULT=$(.github/scripts/fetch-zkvm-version.sh "${{ matrix.zkvm }}" "${{ matrix.crate }}") + + CURRENT=$(echo "$RESULT" | grep "CURRENT=" | cut -d= -f2) + LATEST=$(echo "$RESULT" | grep "LATEST=" | cut -d= -f2) + + if [ -z "$CURRENT" ] || [ -z "$LATEST" ]; then + echo "Failed to fetch version" + exit 1 + fi + + echo "current=$CURRENT" >> $GITHUB_OUTPUT + echo "latest=$LATEST" >> $GITHUB_OUTPUT + + echo "Current version: $CURRENT" + echo "Latest version: $LATEST" + + if [ "$CURRENT" == "$LATEST" ]; then + echo "Up to date" + echo "outdated=false" >> $GITHUB_OUTPUT + else + echo "Update available" + echo "outdated=true" >> $GITHUB_OUTPUT + fi + + - name: Create or update issue if outdated + if: steps.check.outputs.outdated == 'true' + uses: actions/github-script@v7 + env: + ZKVM: ${{ matrix.zkvm }} + CURRENT: ${{ steps.check.outputs.current }} + LATEST: ${{ steps.check.outputs.latest }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const zkvm = process.env.ZKVM; + const current = process.env.CURRENT; + const latest = process.env.LATEST; + + const title = `Update \`${zkvm}\` to \`${latest}\``; + const body = ` + A new version of \`${zkvm}\` is available. + + - Current version: \`${current}\` + - Latest version: \`${latest}\` + `; + + const issues = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + labels: `zkvm:${zkvm}`, + per_page: 100 + }); + + const existingIssue = issues.data.find(issue => + issue.title.startsWith(`Update \`${zkvm}\` to`) + ); + + if (existingIssue) { + if (existingIssue.title === title) { + console.log(`Issue #${existingIssue.number} already exists`); + } else { + await github.rest.issues.update({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: existingIssue.number, + body: body.trim(), + title + }); + console.log(`Updated issue #${existingIssue.number} with new version ${latest}`); + } + } else { + const issue = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title, + body: body.trim(), + labels: [`zkvm:${zkvm}`] + }); + console.log(`Created issue #${issue.data.number}`); + } diff --git a/.github/workflows/test-zkvm.yml b/.github/workflows/test-zkvm.yml index 0fe0575..5909a5f 100644 --- a/.github/workflows/test-zkvm.yml +++ b/.github/workflows/test-zkvm.yml @@ -173,7 +173,7 @@ jobs: /bin/bash" cat <), - #[error("Docker image build failed")] - ImageBuildFailed, - #[error("Docker is not available. Please ensure Docker is installed and running.")] - DockerIsNotAvailable, -} - -pub fn build_image(compiler_dockerfile: &Path, tag: &str) -> Result<(), Error> { - // Check that Docker is installed and available - if Command::new("docker") - .arg("--version") - .stdout(Stdio::null()) - .stderr(Stdio::null()) - .status() - .is_err() - { - return Err(Error::DockerIsNotAvailable); - } - - info!( - "Building Docker image in {} with tag {}", - compiler_dockerfile.display(), - tag - ); - - let cargo_workspace_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join("../..") - .canonicalize() - .unwrap(); - - // Build base image - info!("Building base Docker image..."); - let dockerfile_base_path = cargo_workspace_dir.join("docker/base/Dockerfile.base"); - let status = Command::new("docker") - .args([ - "build", - "-t", - "ere-base:latest", - "-f", - dockerfile_base_path - .to_str() - .ok_or_else(|| Error::InvalidDockerfilePath(dockerfile_base_path.clone()))?, - cargo_workspace_dir.to_str().unwrap(), - ]) - .status() - .map_err(|e| Error::DockerBuildFailed(e.into()))?; - if !status.success() { - return Err(Error::ImageBuildFailed); - } - - info!("Building guest compiler image..."); - let dockerfile_path = cargo_workspace_dir.join(compiler_dockerfile); - let status = Command::new("docker") - .args([ - "build", - "-t", - tag, - "-f", - dockerfile_path - .to_str() - .ok_or_else(|| Error::InvalidDockerfilePath(dockerfile_path.clone()))?, - cargo_workspace_dir.to_str().unwrap(), - ]) - .status() - .map_err(|e| Error::DockerBuildFailed(e.into()))?; - - if !status.success() { - return Err(Error::ImageBuildFailed); - } - - Ok(()) -} - -#[derive(Debug)] -pub struct DockerRunCommand { - image: String, - volumes: Vec<(String, String)>, // (host_path, container_path) - command: Vec, - // remove image after running - remove_after: bool, -} - -impl DockerRunCommand { - pub fn new(image: impl Into) -> Self { - Self { - image: image.into(), - volumes: Vec::new(), - command: Vec::new(), - remove_after: false, - } - } - - pub fn with_volume( - mut self, - host_path: impl Into, - container_path: impl Into, - ) -> Self { - self.volumes.push((host_path.into(), container_path.into())); - self - } - - pub fn with_command(mut self, args: impl IntoIterator>) -> Self { - self.command.extend(args.into_iter().map(|s| s.into())); - self - } - - pub fn remove_after_run(mut self) -> Self { - self.remove_after = true; - self - } - - pub fn to_args(&self) -> Vec { - let mut args = vec!["run".to_string()]; - - if self.remove_after { - args.push("--rm".to_string()); - } - - for (host_path, container_path) in &self.volumes { - args.extend(["-v".to_string(), format!("{host_path}:{container_path}")]); - } - - args.push(self.image.clone()); - args.extend(self.command.iter().cloned()); - - args - } - - pub fn run(&self) -> Result { - Command::new("docker").args(self.to_args()).status() - } -} diff --git a/crates/build-utils/src/lib.rs b/crates/build-utils/src/lib.rs index 6e5fdc2..7d304c0 100644 --- a/crates/build-utils/src/lib.rs +++ b/crates/build-utils/src/lib.rs @@ -3,8 +3,6 @@ use cargo_metadata::MetadataCommand; use std::{env, fs, path::Path}; -pub mod docker; - // Detect and generate a Rust source file that contains the name and version of the SDK. pub fn detect_and_generate_name_and_sdk_version(name: &str, sdk_dep_name: &str) { gen_name_and_sdk_version(name, &detect_sdk_version(sdk_dep_name));