diff --git a/scripts/.gitignore b/scripts/.gitignore new file mode 100644 index 000000000..f9606a377 --- /dev/null +++ b/scripts/.gitignore @@ -0,0 +1 @@ +/venv diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 000000000..66b979d05 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,93 @@ +# Release Automation Scripts + +This directory contains scripts to automate the risc0-ethereum release process as described in [RELEASE.md](../RELEASE.md). + +## Quick Start + +### Prerequisites + +- Python 3.7+ +- Git +- Bash + +### Setup + +1. Set up the Python environment: +```bash +scripts/setup-venv.sh +``` + +2. Run a release preparation (dry-run first!): +```bash +# Prepare release 2.1.0 (dry run) +scripts/prepare-release.sh 2.1.0 --dry-run + +# Actually run it +scripts/prepare-release.sh 2.1.0 +``` + +3. Prepare next development version: +```bash +# Prepare next dev version 2.2.0-alpha.1 (dry run) +scripts/prepare-next-version.sh 2.2.0 --dry-run + +# Actually run it +scripts/prepare-next-version.sh 2.2.0 +``` + +## Main Orchestration Scripts + +### `prepare-release.sh` +Automates the complete release branch preparation process. + +**Usage:** +```bash +scripts/prepare-release.sh [options] + +Arguments: + release_version Version to release (e.g., 2.1.0) + +Options: + --risc0-version RISC Zero monorepo version to use (default: 2.0) + --dry-run Show changes without applying them + --help Show help message + +Examples: + scripts/prepare-release.sh 2.1.0 + scripts/prepare-release.sh 2.1.0 --risc0-version 2.0 --dry-run +``` + +**What it does:** +1. Creates release branch (e.g., `release-2.1`) +2. Updates all Cargo.toml versions to release version +3. Updates contract version strings +4. Updates branch references from `main` to release branch +5. Converts risc0 git dependencies to version dependencies +6. Updates .gitignore to include Cargo.lock files +7. Removes main branch warning from README.md +8. Updates CHANGELOG.md to mark current version as released + +### `prepare-next-version.sh` +Prepares the main branch for the next development cycle. + +**Usage:** +```bash +scripts/prepare-next-version.sh [options] + +Arguments: + next_version Next development version (e.g., 2.2.0) + Script automatically appends '-alpha.1' + +Options: + --dry-run Show changes without applying them + --help Show help message + +Examples: + scripts/prepare-next-version.sh 2.2.0 + scripts/prepare-next-version.sh 2.2.0 --dry-run +``` + +**What it does:** +1. Updates all Cargo.toml versions to next alpha version +2. Updates contract version strings to next alpha version +3. Adds new "Unreleased" section to CHANGELOG.md \ No newline at end of file diff --git a/scripts/bump-cargo-versions.py b/scripts/bump-cargo-versions.py new file mode 100755 index 000000000..c579c760d --- /dev/null +++ b/scripts/bump-cargo-versions.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python3 +""" +Cargo version bump script for risc0-ethereum release automation. + +This script updates versions across all workspace Cargo.toml files. +Supports both release mode (x.y.z) and next development mode (x.y+1.0-alpha.1). +""" + +import argparse +import re +import sys +import toml +from pathlib import Path +from typing import List, Tuple, Optional + + +def parse_version(version_str: str) -> Tuple[int, int, int, Optional[str]]: + """Parse semantic version string into components.""" + pattern = r'^(\d+)\.(\d+)\.(\d+)(?:-(.+))?$' + match = re.match(pattern, version_str) + if not match: + raise ValueError(f"Invalid version format: {version_str}") + + major, minor, patch, prerelease = match.groups() + return int(major), int(minor), int(patch), prerelease + + +def format_version(major: int, minor: int, patch: int, prerelease: Optional[str] = None) -> str: + """Format version components back to string.""" + version = f"{major}.{minor}.{patch}" + if prerelease: + version += f"-{prerelease}" + return version + + +def get_next_release_version(current_version: str) -> str: + """Convert alpha version to release version (remove prerelease suffix).""" + major, minor, patch, prerelease = parse_version(current_version) + if prerelease and 'alpha' in prerelease: + # Remove alpha suffix for release + return format_version(major, minor, patch) + else: + # Already a release version, just return as-is + return current_version + + +def get_next_dev_version(current_version: str) -> str: + """Get next development version (increment minor, set patch to 0, add alpha.1).""" + major, minor, patch, prerelease = parse_version(current_version) + # For next dev version, increment minor and reset patch + return format_version(major, minor + 1, 0, "alpha.1") + + +def find_cargo_toml_files() -> List[Path]: + """Find all Cargo.toml files that need version updates.""" + root = Path('.') + + # Use the grep command from RELEASE.md to find files + import subprocess + result = subprocess.run([ + 'grep', '-rl', '^version = "', '--include=Cargo.toml', + '--exclude-dir=./lib', '--exclude-dir=./examples', + '--exclude-dir=./crates/ffi/guests', '--exclude-dir=./target', '.' + ], capture_output=True, text=True, cwd=root) + + if result.returncode != 0: + return [] + + files = [Path(f.strip()) for f in result.stdout.strip().split('\n') if f.strip()] + return [f for f in files if f.exists()] + + +def update_cargo_toml(file_path: Path, new_version: str, dry_run: bool = False) -> bool: + """Update version in a Cargo.toml file.""" + if not file_path.exists(): + print(f"Warning: {file_path} does not exist") + return False + + try: + content = file_path.read_text() + data = toml.loads(content) + + # Check if this file has a version field + if 'package' in data and 'version' in data['package']: + old_version = data['package']['version'] + if old_version != new_version: + print(f" {file_path}: {old_version} -> {new_version}") + if not dry_run: + data['package']['version'] = new_version + file_path.write_text(toml.dumps(data)) + return True + + # Check workspace version + if 'workspace' in data and 'package' in data['workspace'] and 'version' in data['workspace']['package']: + old_version = data['workspace']['package']['version'] + if old_version != new_version: + print(f" {file_path} (workspace): {old_version} -> {new_version}") + if not dry_run: + data['workspace']['package']['version'] = new_version + file_path.write_text(toml.dumps(data)) + return True + + except Exception as e: + print(f"Error updating {file_path}: {e}") + return False + + return False + + +def main(): + parser = argparse.ArgumentParser(description='Bump Cargo.toml versions for risc0-ethereum release') + parser.add_argument('version', help='Target version (e.g., 2.1.0 or auto)') + parser.add_argument('--mode', choices=['release', 'next-dev'], required=True, + help='release: prepare for release, next-dev: prepare for next development cycle') + parser.add_argument('--dry-run', action='store_true', help='Show changes without applying them') + + args = parser.parse_args() + + # Read current version from workspace Cargo.toml + workspace_cargo = Path('Cargo.toml') + if not workspace_cargo.exists(): + print("Error: Cargo.toml not found in current directory") + sys.exit(1) + + try: + workspace_data = toml.loads(workspace_cargo.read_text()) + current_version = workspace_data['workspace']['package']['version'] + print(f"Current version: {current_version}") + except Exception as e: + print(f"Error reading workspace Cargo.toml: {e}") + sys.exit(1) + + # Determine target version + if args.version == 'auto': + if args.mode == 'release': + target_version = get_next_release_version(current_version) + else: # next-dev + target_version = get_next_dev_version(current_version) + else: + target_version = args.version + + print(f"Target version: {target_version}") + + if args.dry_run: + print("\n=== DRY RUN MODE - No changes will be made ===") + + # Validate target version format + try: + parse_version(target_version) + except ValueError as e: + print(f"Error: {e}") + sys.exit(1) + + changes_made = False + + # Update Cargo.toml files + print(f"\nUpdating Cargo.toml files:") + cargo_files = find_cargo_toml_files() + if not cargo_files: + print("No Cargo.toml files found") + else: + for file_path in cargo_files: + if update_cargo_toml(file_path, target_version, args.dry_run): + changes_made = True + + if not changes_made: + print("\nNo changes needed - versions are already up to date") + elif args.dry_run: + print(f"\nDry run complete. Run without --dry-run to apply changes.") + else: + print(f"\nCargo version bump complete: {current_version} -> {target_version}") + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/scripts/bump-contract-versions.py b/scripts/bump-contract-versions.py new file mode 100755 index 000000000..c98b82844 --- /dev/null +++ b/scripts/bump-contract-versions.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +""" +Contract version bump script for risc0-ethereum release automation. + +This script updates version strings in Solidity contract files. +""" + +import argparse +import re +import sys +from pathlib import Path +from typing import List + + +def find_contract_files() -> List[Path]: + """Find contract files that contain version strings.""" + return [ + Path('contracts/src/groth16/RiscZeroGroth16Verifier.sol'), + Path('contracts/src/RiscZeroSetVerifier.sol') + ] + + +def update_contract_version(file_path: Path, new_version: str, dry_run: bool = False) -> bool: + """Update version string in a Solidity contract file.""" + if not file_path.exists(): + print(f"Warning: {file_path} does not exist") + return False + + try: + content = file_path.read_text() + + # Pattern to match version strings in contracts + pattern = r'string public constant VERSION = "([^"]+)";' + + changes_made = False + def replace_version(match): + nonlocal changes_made + old_version = match.group(1) + if old_version != new_version: + print(f" {file_path}: {old_version} -> {new_version}") + changes_made = True + return f'string public constant VERSION = "{new_version}";' + return match.group(0) + + new_content = re.sub(pattern, replace_version, content) + + if changes_made and not dry_run: + file_path.write_text(new_content) + + return changes_made + + except Exception as e: + print(f"Error updating {file_path}: {e}") + return False + + +def main(): + parser = argparse.ArgumentParser(description='Bump contract version strings for risc0-ethereum release') + parser.add_argument('version', help='Target version (e.g., 2.1.0)') + parser.add_argument('--dry-run', action='store_true', help='Show changes without applying them') + + args = parser.parse_args() + + if args.dry_run: + print("=== DRY RUN MODE - No changes will be made ===") + + print(f"Target version: {args.version}") + + changes_made = False + + # Update contract files + print(f"\nUpdating contract version strings:") + contract_files = find_contract_files() + for file_path in contract_files: + if update_contract_version(file_path, args.version, args.dry_run): + changes_made = True + + if not changes_made: + print("\nNo changes needed - contract versions are already up to date") + elif args.dry_run: + print(f"\nDry run complete. Run without --dry-run to apply changes.") + else: + print(f"\nContract version bump complete -> {args.version}") + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/scripts/prepare-next-version.sh b/scripts/prepare-next-version.sh new file mode 100755 index 000000000..d2d08fcf3 --- /dev/null +++ b/scripts/prepare-next-version.sh @@ -0,0 +1,168 @@ +#!/bin/bash +# Next version preparation script for risc0-ethereum. +# +# This script prepares the main branch for the next development cycle by: +# 1. Bumping versions to next alpha +# 2. Updating contract versions +# 3. Adding new CHANGELOG.md section + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Print functions +print_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +print_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Default values +DRY_RUN=false +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +VENV_DIR="$SCRIPT_DIR/venv" + +# Function to ensure Python environment is set up +ensure_python_env() { + if [[ ! -d "$VENV_DIR" ]]; then + print_info "Setting up Python virtual environment..." + "$SCRIPT_DIR/setup-venv.sh" + fi +} + +# Function to run Python scripts with the virtual environment +run_python() { + source "$VENV_DIR/bin/activate" + python3 "$@" +} + +# Usage function +usage() { + cat << EOF +Usage: $0 [options] + +Arguments: + next_version Next development version (e.g., 2.2.0) + +Options: + --dry-run Show changes without applying them + --help Show this help message + +Examples: + $0 2.2.0 + $0 2.2.0 --dry-run + +Note: The script will automatically append '-alpha.1' to the provided version. +EOF +} + +# Parse arguments +NEXT_VERSION="" +while [[ $# -gt 0 ]]; do + case $1 in + --dry-run) + DRY_RUN=true + shift + ;; + --help) + usage + exit 0 + ;; + *) + if [[ -z "$NEXT_VERSION" ]]; then + NEXT_VERSION="$1" + else + print_error "Unknown argument: $1" + usage + exit 1 + fi + shift + ;; + esac +done + +# Validate required arguments +if [[ -z "$NEXT_VERSION" ]]; then + print_error "Next version is required" + usage + exit 1 +fi + +# Validate version format and append alpha suffix +if ! echo "$NEXT_VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then + print_error "Invalid version format. Expected: x.y.z (e.g., 2.2.0)" + exit 1 +fi + +NEXT_ALPHA_VERSION="$NEXT_VERSION-alpha.1" + +print_info "Preparing next development version: $NEXT_ALPHA_VERSION" + +if [[ "$DRY_RUN" == "true" ]]; then + print_warn "DRY RUN MODE - No changes will be made" +fi + +# Check if we're in the right directory +if [[ ! -f "Cargo.toml" ]] || [[ ! -d "contracts" ]]; then + print_error "This script must be run from the risc0-ethereum repository root" + exit 1 +fi + + +# Ensure Python environment is ready +ensure_python_env + +print_info "Step 1: Updating Cargo.toml versions to $NEXT_ALPHA_VERSION" +if [[ "$DRY_RUN" == "true" ]]; then + run_python "$SCRIPT_DIR/bump-cargo-versions.py" "$NEXT_ALPHA_VERSION" --mode next-dev --dry-run +else + run_python "$SCRIPT_DIR/bump-cargo-versions.py" "$NEXT_ALPHA_VERSION" --mode next-dev +fi + +print_info "Step 2: Updating contract versions to $NEXT_ALPHA_VERSION" +if [[ "$DRY_RUN" == "true" ]]; then + run_python "$SCRIPT_DIR/bump-contract-versions.py" "$NEXT_ALPHA_VERSION" --dry-run +else + run_python "$SCRIPT_DIR/bump-contract-versions.py" "$NEXT_ALPHA_VERSION" +fi + +print_info "Step 3: Adding new Unreleased section to CHANGELOG.md" +CHANGELOG_FILE="crates/steel/CHANGELOG.md" +if ! grep -q "## \[Unreleased\]" "$CHANGELOG_FILE"; then + if [[ "$DRY_RUN" == "true" ]]; then + echo "[DRY RUN] Would add new Unreleased section to $CHANGELOG_FILE" + else + print_info "Adding new Unreleased section to $CHANGELOG_FILE" + # Find the line with "All notable changes" and add the new section after it + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + sed -i '' '/All notable changes to this project will be documented in this file./a\ +\ +## [Unreleased]\ +' "$CHANGELOG_FILE" + else + # Linux + sed -i '/All notable changes to this project will be documented in this file./a\\n## [Unreleased]\n' "$CHANGELOG_FILE" + fi + fi +else + print_info "CHANGELOG.md already has Unreleased section" +fi + +print_info "Next version preparation complete!" +print_info "" +print_info "Next steps:" +print_info "1. Review the changes: git status && git diff" +print_info "2. Commit the changes: git add -A && git commit -m 'Prepare next development version $NEXT_ALPHA_VERSION'" +print_info "3. Push to main: git push origin main" +print_info "4. Create a PR for the main branch changes" diff --git a/scripts/prepare-release.sh b/scripts/prepare-release.sh new file mode 100755 index 000000000..271f2e869 --- /dev/null +++ b/scripts/prepare-release.sh @@ -0,0 +1,238 @@ +#!/bin/bash +# Release preparation orchestration script for risc0-ethereum. +# +# This script automates the release branch preparation process by: +# 1. Creating a release branch +# 2. Updating versions +# 3. Updating branch references +# 4. Updating dependencies +# 5. Updating other release-specific files + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Print functions +print_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +print_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Default values +DRY_RUN=false +RISC0_VERSION="2.0" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +VENV_DIR="$SCRIPT_DIR/venv" + +# Function to ensure Python environment is set up +ensure_python_env() { + if [[ ! -d "$VENV_DIR" ]]; then + print_info "Setting up Python virtual environment..." + "$SCRIPT_DIR/setup-venv.sh" + fi +} + +# Function to run Python scripts with the virtual environment +run_python() { + source "$VENV_DIR/bin/activate" + python3 "$@" +} + +# Usage function +usage() { + cat << EOF +Usage: $0 [options] + +Arguments: + release_version Version to release (e.g., 2.1.0) + +Options: + --risc0-version RISC Zero monorepo version to use (default: 2.0) + --dry-run Show changes without applying them + --help Show this help message + +Examples: + $0 2.1.0 + $0 2.1.0 --risc0-version 2.0 --dry-run +EOF +} + +# Parse arguments +RELEASE_VERSION="" +while [[ $# -gt 0 ]]; do + case $1 in + --dry-run) + DRY_RUN=true + shift + ;; + --risc0-version) + RISC0_VERSION="$2" + shift 2 + ;; + --help) + usage + exit 0 + ;; + *) + if [[ -z "$RELEASE_VERSION" ]]; then + RELEASE_VERSION="$1" + else + print_error "Unknown argument: $1" + usage + exit 1 + fi + shift + ;; + esac +done + +# Validate required arguments +if [[ -z "$RELEASE_VERSION" ]]; then + print_error "Release version is required" + usage + exit 1 +fi + +# Validate release version format +if ! echo "$RELEASE_VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+$'; then + print_error "Invalid release version format. Expected: x.y.z (e.g., 2.1.0)" + exit 1 +fi + +# Extract major.minor for branch name +MAJOR_MINOR=$(echo "$RELEASE_VERSION" | sed 's/\.[0-9]*$//') +RELEASE_BRANCH="release-$MAJOR_MINOR" + +print_info "Preparing release $RELEASE_VERSION" +print_info "Release branch: $RELEASE_BRANCH" +print_info "RISC Zero version: $RISC0_VERSION" + +if [[ "$DRY_RUN" == "true" ]]; then + print_warn "DRY RUN MODE - No changes will be made" +fi + +# Check if we're in the right directory +if [[ ! -f "Cargo.toml" ]] || [[ ! -d "contracts" ]]; then + print_error "This script must be run from the risc0-ethereum repository root" + exit 1 +fi + +# Function to run commands with dry-run support +run_cmd() { + if [[ "$DRY_RUN" == "true" ]]; then + echo "[DRY RUN] Would run: $*" + else + print_info "Running: $*" + "$@" + fi +} + +# Function to update files with dry-run support +update_file() { + local file="$1" + local old_content="$2" + local new_content="$3" + + if [[ "$DRY_RUN" == "true" ]]; then + echo "[DRY RUN] Would update $file:" + echo " - $old_content" + echo " + $new_content" + else + print_info "Updating $file" + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + sed -i '' "s/$old_content/$new_content/g" "$file" + else + # Linux + sed -i "s/$old_content/$new_content/g" "$file" + fi + fi +} + +# Ensure Python environment is ready +ensure_python_env + +print_info "Step 1: Updating Cargo.toml versions" +if [[ "$DRY_RUN" == "true" ]]; then + run_python "$SCRIPT_DIR/bump-cargo-versions.py" "$RELEASE_VERSION" --mode release --dry-run +else + run_python "$SCRIPT_DIR/bump-cargo-versions.py" "$RELEASE_VERSION" --mode release +fi + +print_info "Step 2: Updating contract versions" +if [[ "$DRY_RUN" == "true" ]]; then + run_python "$SCRIPT_DIR/bump-contract-versions.py" "$RELEASE_VERSION" --dry-run +else + run_python "$SCRIPT_DIR/bump-contract-versions.py" "$RELEASE_VERSION" +fi + +print_info "Step 3: Updating branch references" +if [[ "$DRY_RUN" == "true" ]]; then + run_python "$SCRIPT_DIR/update-branch-refs.py" "$RELEASE_BRANCH" --dry-run +else + run_python "$SCRIPT_DIR/update-branch-refs.py" "$RELEASE_BRANCH" +fi + +print_info "Step 4: Updating risc0 dependencies" +if [[ "$DRY_RUN" == "true" ]]; then + run_python "$SCRIPT_DIR/update-dependencies.py" "$RISC0_VERSION" --dry-run +else + run_python "$SCRIPT_DIR/update-dependencies.py" "$RISC0_VERSION" +fi + +print_info "Step 5: Updating .gitignore to include Cargo.lock files" +if grep -q "^Cargo.lock$" .gitignore; then + update_file ".gitignore" "^Cargo.lock$" "" + update_file ".gitignore" "^# We ignore lock files.*" "" + update_file ".gitignore" "^# continually track.*" "" + update_file ".gitignore" "^# tracked, CI will.*" "" +else + print_info ".gitignore already configured for release" +fi + +print_info "Step 6: Updating README.md" +if grep -q "main.*is the development branch" README.md; then + # Remove the main branch warning + if [[ "$DRY_RUN" == "true" ]]; then + echo "[DRY RUN] Would remove main branch warning from README.md" + else + print_info "Removing main branch warning from README.md" + if [[ "$OSTYPE" == "darwin"* ]]; then + # macOS + sed -i '' '/> \[!IMPORTANT\]/,/^$/d' README.md + else + # Linux + sed -i '/> \[!IMPORTANT\]/,/^$/d' README.md + fi + fi +else + print_info "README.md already configured for release" +fi + +print_info "Step 7: Updating CHANGELOG.md" +if grep -q "## \[Unreleased\]" crates/steel/CHANGELOG.md; then + update_file "crates/steel/CHANGELOG.md" "## \[Unreleased\]" "## [$RELEASE_VERSION](https://github.com/risc0/risc0-ethereum/releases/tag/v$RELEASE_VERSION)" +else + print_info "CHANGELOG.md already configured for release" +fi + +print_info "Release preparation complete!" +print_info "" +print_info "Next steps:" +print_info "1. Review the changes: git status && git diff" +print_info "2. Commit the changes: git add -A && git commit -m 'Prepare release $RELEASE_VERSION'" +print_info "3. Push the release branch: git push origin $RELEASE_BRANCH" +print_info "4. Create a PR to the release branch" +print_info "5. After the PR is merged, tag the release: git tag v$RELEASE_VERSION" +print_info "6. Create a GitHub release with release notes" diff --git a/scripts/requirements.txt b/scripts/requirements.txt new file mode 100644 index 000000000..9a3f6e5c5 --- /dev/null +++ b/scripts/requirements.txt @@ -0,0 +1 @@ +toml>=0.10.2 \ No newline at end of file diff --git a/scripts/setup-venv.sh b/scripts/setup-venv.sh new file mode 100755 index 000000000..0727b7bf1 --- /dev/null +++ b/scripts/setup-venv.sh @@ -0,0 +1,32 @@ +#!/bin/bash +""" +Setup Python virtual environment for release automation scripts. +""" + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +VENV_DIR="$SCRIPT_DIR/venv" + +echo "Setting up Python virtual environment for release scripts..." + +# Create virtual environment if it doesn't exist +if [[ ! -d "$VENV_DIR" ]]; then + echo "Creating virtual environment..." + python3 -m venv "$VENV_DIR" +fi + +# Activate virtual environment +echo "Activating virtual environment..." +source "$VENV_DIR/bin/activate" + +# Install dependencies +echo "Installing Python dependencies..." +pip install -r "$SCRIPT_DIR/requirements.txt" + +echo "Setup complete!" +echo "" +echo "To use the scripts:" +echo "1. Activate the environment: source scripts/venv/bin/activate" +echo "2. Run scripts normally: python3 scripts/bump-cargo-versions.py ..." +echo "3. Or use the wrapper scripts that auto-activate the environment" \ No newline at end of file diff --git a/scripts/update-branch-refs.py b/scripts/update-branch-refs.py new file mode 100755 index 000000000..c77223639 --- /dev/null +++ b/scripts/update-branch-refs.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 +""" +Branch reference update script for risc0-ethereum release automation. + +This script updates branch references from 'main' to a release branch across +GitHub workflows, README files, and other locations. +""" + +import argparse +import re +import sys +from pathlib import Path +from typing import List, Tuple + + +def find_github_workflow_files() -> List[Path]: + """Find GitHub workflow files that may contain branch references.""" + workflow_dir = Path('.github/workflows') + if not workflow_dir.exists(): + return [] + + return list(workflow_dir.glob('*.yml')) + list(workflow_dir.glob('*.yaml')) + + +def find_readme_files() -> List[Path]: + """Find README files that may contain branch references.""" + files = [] + for pattern in ['README.md', '**/README.md']: + files.extend(Path('.').glob(pattern)) + return files + + +def update_risc0_monorepo_ref(file_path: Path, release_branch: str, dry_run: bool = False) -> bool: + """Update RISC0_MONOREPO_REF from 'main' to release branch.""" + if not file_path.exists(): + return False + + try: + content = file_path.read_text() + + # Pattern to match RISC0_MONOREPO_REF: "main" + pattern = r'RISC0_MONOREPO_REF:\s*["\']main["\']' + replacement = f'RISC0_MONOREPO_REF: "{release_branch}"' + + new_content = re.sub(pattern, replacement, content) + + if new_content != content: + print(f" {file_path}: RISC0_MONOREPO_REF main -> {release_branch}") + if not dry_run: + file_path.write_text(new_content) + return True + + except Exception as e: + print(f"Error updating {file_path}: {e}") + return False + + return False + + +def update_github_refs(file_path: Path, release_branch: str, dry_run: bool = False) -> bool: + """Update risc0/risc0-ethereum/refs/heads/main references.""" + if not file_path.exists(): + return False + + try: + content = file_path.read_text() + + # Pattern to match risc0/risc0-ethereum/refs/heads/main + pattern = r'risc0/risc0-ethereum/refs/heads/main' + replacement = f'risc0/risc0-ethereum/refs/heads/{release_branch}' + + new_content = re.sub(pattern, replacement, content) + + if new_content != content: + print(f" {file_path}: refs/heads/main -> refs/heads/{release_branch}") + if not dry_run: + file_path.write_text(new_content) + return True + + except Exception as e: + print(f"Error updating {file_path}: {e}") + return False + + return False + + +def update_create_steel_app_urls(file_path: Path, release_branch: str, dry_run: bool = False) -> bool: + """Update create-steel-app script URLs from main to release branch.""" + if not file_path.exists(): + return False + + try: + content = file_path.read_text() + + # Pattern to match create-steel-app URLs + pattern = r'https://raw\.githubusercontent\.com/risc0/risc0-ethereum/refs/heads/main/crates/steel/docs/create-steel-app/create-steel-app' + replacement = f'https://raw.githubusercontent.com/risc0/risc0-ethereum/refs/heads/{release_branch}/crates/steel/docs/create-steel-app/create-steel-app' + + new_content = re.sub(pattern, replacement, content) + + if new_content != content: + print(f" {file_path}: create-steel-app URL main -> {release_branch}") + if not dry_run: + file_path.write_text(new_content) + return True + + except Exception as e: + print(f"Error updating {file_path}: {e}") + return False + + return False + + +def main(): + parser = argparse.ArgumentParser(description='Update branch references for risc0-ethereum release') + parser.add_argument('release_branch', help='Release branch name (e.g., release-2.1)') + parser.add_argument('--dry-run', action='store_true', help='Show changes without applying them') + + args = parser.parse_args() + + if args.dry_run: + print("=== DRY RUN MODE - No changes will be made ===") + + print(f"Updating branch references to: {args.release_branch}") + + changes_made = False + + # Update GitHub workflow files + print(f"\nUpdating GitHub workflow files:") + workflow_files = find_github_workflow_files() + if not workflow_files: + print(" No workflow files found") + else: + for file_path in workflow_files: + if update_risc0_monorepo_ref(file_path, args.release_branch, args.dry_run): + changes_made = True + + # Update README files for GitHub refs + print(f"\nUpdating GitHub refs in README files:") + readme_files = find_readme_files() + if not readme_files: + print(" No README files found") + else: + for file_path in readme_files: + if update_github_refs(file_path, args.release_branch, args.dry_run): + changes_made = True + + # Update create-steel-app URLs + print(f"\nUpdating create-steel-app URLs:") + for file_path in readme_files: + if update_create_steel_app_urls(file_path, args.release_branch, args.dry_run): + changes_made = True + + if not changes_made: + print("\nNo changes needed - branch references are already up to date") + elif args.dry_run: + print(f"\nDry run complete. Run without --dry-run to apply changes.") + else: + print(f"\nBranch reference update complete -> {args.release_branch}") + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/scripts/update-dependencies.py b/scripts/update-dependencies.py new file mode 100755 index 000000000..32b2d6be1 --- /dev/null +++ b/scripts/update-dependencies.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 +""" +Dependency update script for risc0-ethereum release automation. + +This script updates risc0 monorepo dependencies from git references to published versions, +and runs cargo update in all workspaces. +""" + +import argparse +import subprocess +import sys +import toml +from pathlib import Path +from typing import List, Dict, Any + + +def find_workspace_cargo_files() -> List[Path]: + """Find all workspace root Cargo.toml files.""" + # Use the grep command from RELEASE.md to find workspace files + result = subprocess.run([ + 'grep', '-rl', '--include=Cargo.toml', r'\[workspace\]', + '--exclude-dir=./lib', '.' + ], capture_output=True, text=True) + + if result.returncode != 0: + return [] + + files = [Path(f.strip()) for f in result.stdout.strip().split('\n') if f.strip()] + return [f for f in files if f.exists()] + + +def update_risc0_dependencies(file_path: Path, risc0_version: str, dry_run: bool = False) -> bool: + """Update risc0 git dependencies to version dependencies in a Cargo.toml file.""" + if not file_path.exists(): + return False + + try: + content = file_path.read_text() + data = toml.loads(content) + + changes_made = False + + # Check workspace dependencies + if 'workspace' in data and 'dependencies' in data['workspace']: + deps = data['workspace']['dependencies'] + for dep_name, dep_info in deps.items(): + if dep_name.startswith('risc0-') and isinstance(dep_info, dict): + # Check if it's a git dependency pointing to risc0 repo + if 'git' in dep_info and 'risc0/risc0' in dep_info['git']: + print(f" {file_path}: {dep_name} git -> version {risc0_version}") + if not dry_run: + # Remove git and branch, add version + dep_info.pop('git', None) + dep_info.pop('branch', None) + dep_info['version'] = risc0_version + changes_made = True + + # Check regular dependencies + if 'dependencies' in data: + deps = data['dependencies'] + for dep_name, dep_info in deps.items(): + if dep_name.startswith('risc0-') and isinstance(dep_info, dict): + # Check if it's a git dependency pointing to risc0 repo + if 'git' in dep_info and 'risc0/risc0' in dep_info['git']: + print(f" {file_path}: {dep_name} git -> version {risc0_version}") + if not dry_run: + # Remove git and branch, add version + dep_info.pop('git', None) + dep_info.pop('branch', None) + dep_info['version'] = risc0_version + changes_made = True + + if changes_made and not dry_run: + file_path.write_text(toml.dumps(data)) + + return changes_made + + except Exception as e: + print(f"Error updating {file_path}: {e}") + return False + + +def run_cargo_update(workspace_dir: Path, dry_run: bool = False) -> bool: + """Run cargo update in a workspace directory.""" + if not workspace_dir.exists(): + return False + + cargo_toml = workspace_dir / 'Cargo.toml' + if not cargo_toml.exists(): + return False + + print(f" Running cargo update in {workspace_dir}") + + if dry_run: + print(f" (dry run - would run: cargo update --manifest-path {cargo_toml})") + return True + + try: + result = subprocess.run([ + 'cargo', 'update', '--manifest-path', str(cargo_toml) + ], capture_output=True, text=True, cwd=workspace_dir) + + if result.returncode != 0: + print(f" Error running cargo update: {result.stderr}") + return False + + if result.stdout.strip(): + print(f" {result.stdout.strip()}") + + return True + + except Exception as e: + print(f" Error running cargo update: {e}") + return False + + +def main(): + parser = argparse.ArgumentParser(description='Update risc0 dependencies for risc0-ethereum release') + parser.add_argument('risc0_version', help='RISC Zero version to use (e.g., 2.0)') + parser.add_argument('--dry-run', action='store_true', help='Show changes without applying them') + + args = parser.parse_args() + + if args.dry_run: + print("=== DRY RUN MODE - No changes will be made ===") + + print(f"Updating risc0 dependencies to version: {args.risc0_version}") + + changes_made = False + + # Find all workspace Cargo.toml files + print(f"\nFinding workspace Cargo.toml files:") + workspace_files = find_workspace_cargo_files() + if not workspace_files: + print(" No workspace files found") + return + + for file_path in workspace_files: + print(f" Found workspace: {file_path}") + + # Update dependencies in workspace files + print(f"\nUpdating risc0 dependencies:") + for file_path in workspace_files: + if update_risc0_dependencies(file_path, args.risc0_version, args.dry_run): + changes_made = True + + # Run cargo update in each workspace + print(f"\nRunning cargo update in workspaces:") + for file_path in workspace_files: + workspace_dir = file_path.parent + run_cargo_update(workspace_dir, args.dry_run) + + if not changes_made: + print("\nNo dependency changes needed") + elif args.dry_run: + print(f"\nDry run complete. Run without --dry-run to apply changes.") + else: + print(f"\nDependency update complete -> risc0 {args.risc0_version}") + + +if __name__ == '__main__': + main() \ No newline at end of file