Skip to content

Commit

Permalink
Merge pull request #89 from paseo-network/feat/runtime_patch
Browse files Browse the repository at this point in the history
feat: runtime patching scripts
  • Loading branch information
hbulgarini authored Jul 11, 2024
2 parents 3376b62 + da1b3b1 commit eb05342
Show file tree
Hide file tree
Showing 6 changed files with 480 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,5 @@ runtime/wasm/target/
substrate.code-workspace
target/
**/tmp_runtime
**/tmp_hub
**/tmp_hub
*.log
77 changes: 77 additions & 0 deletions scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Runtime Patch Scripts

This directory contains scripts for managing runtime patches:

1. `create_runtime_patch.sh`: Creates patch files with the new code from the Polkadot repo.
2. `apply_runtime_patch.py`: Applies patches while preserving specific content.
3. `revert_unwanted_changes.py`: Reverts specific changes in files.

## Creating the runtime patch files

Creates patch files for Paseo-specific modifications based on the differences between Polkadot runtime versions.

Usage:

```bash
./scripts/create_runtime_patch.sh <current_version> <new_version> [process_parachains]
```

Parameters:

- `current_version`: The current Paseo runtime version
- `new_version`: The new Polkadot runtime version to update to
- `process_parachains`: Optional. Set to 'true' to process parachains. Defaults to 'false'.

Example:

```bash
# Without processing parachains
./scripts/create_runtime_patch.sh 1.2.3 1.2.4

# With processing parachains
./scripts/create_runtime_patch.sh 1.2.3 1.2.4 true
```

This script will create the following patch files in the `patches` directory:

- `relay_polkadot.patch`: Contains changes for the relay chain and Cargo.toml
- `parachain_<name>.patch`: Created for each specified parachain if `process_parachains` is set to true
- `system_parachains_common.patch`: Contains changes for the `system-parachains/constants` directory and `system-parachains/Cargo.toml` file

## Apply the patch

Usage:

```bash
python apply_runtime_patch.py [--check] <patch_file>

The --check option performs a dry run of the patch application process
```

Examples:

```bash
# Apply a patch
python apply_runtime_patch.py ../patches/relay_polkadot.patch

# Check if a patch can be applied
python apply_runtime_patch.py --check ../patches/relay_polkadot.patch
```

## Revert unwanted changes

Reverts specific changes in files based on predefined rules.

Usage:

```bash
python revert_unwanted_changes.py <config.json>
```

Example:

```bash
python revert_unwanted_changes.py replacement_config.json
```

Both `apply_runtime_patch.py` and `revert_unwanted_changes.py` scripts log their actions to `apply_patch.log` and `revert_unwanted_changes.log` respectively in the current directory.
131 changes: 131 additions & 0 deletions scripts/apply_runtime_patch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import sys
import os
import subprocess
import tempfile
import re
from unidiff import PatchSet

# Global variable for the log file name
LOG_FILE = "apply_patch.log"

def keep_sudo_filter(file_path, hunk):
"""
Filter to keep files and lines related to sudo.
- Ignores deletion of files with 'sudo' in their name.
- Prevents deletion of lines containing 'sudo'.
"""
if hunk.source_start == 0 and hunk.source_length == 0 and 'sudo' in file_path.lower():
log(f" Keeping file {file_path}: Contains 'sudo' in filename")
return False

for line in hunk:
if line.is_removed and 'sudo' in line.value.lower():
log(f" Keeping line in {file_path}: Contains 'sudo'")
return False

return True

def log(message):
"""Log a message to both console and the log file."""
print(message)
with open(LOG_FILE, "a") as log_file:
log_file.write(message + "\n")

def filter_hunk(file_path, hunk):
"""Main filter function that applies all individual filters."""
filters = [
keep_sudo_filter
]

for filter_func in filters:
if not filter_func(file_path, hunk):
return False
return True

def apply_hunk_with_git(file_path, hunk_content):
"""Apply a single hunk using git apply."""
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.patch') as temp_file:
# Format the hunk content as a proper patch file
patch_content = f"diff --git a/{file_path} b/{file_path}\n"
patch_content += "--- a/{}\n+++ b/{}\n".format(file_path, file_path)
patch_content += hunk_content
temp_file.write(patch_content)
temp_file_path = temp_file.name

try:
result = subprocess.run(['git', 'apply', '--3way', temp_file_path],
capture_output=True, text=True, check=True)
log(f"Successfully applied hunk to {file_path}")
return True
except subprocess.CalledProcessError as e:
log(f"Failed to apply hunk to {file_path}: {e.stderr}")
return False
finally:
os.unlink(temp_file_path)

def apply_patch_line_by_line(patch_file, check_only=False, hunk_filter=filter_hunk):
try:
with open(patch_file, 'r') as pf:
patch = PatchSet(pf)

modified_files = set()

for patched_file in patch:
file_path = patched_file.path
for hunk in patched_file:
if not hunk_filter(file_path, hunk):
log(f"Skipping hunk in {file_path} due to filter")
continue

hunk_content = str(hunk)
if not check_only:
success = apply_hunk_with_git(file_path, hunk_content)
if not success:
log(f"Failed to apply hunk to {file_path}")
return False
modified_files.add(file_path)
else:
log(f"Hunk for {file_path} can be applied")

if not check_only:
# Reset all modified files
for file_path in modified_files:
try:
reset_result = subprocess.run(['git', 'reset', file_path],
capture_output=True, text=True, check=True)
log(f"Reset {file_path} to unstage changes")
except subprocess.CalledProcessError as e:
log(f"Failed to reset {file_path}: {e.stderr}")
return False

log("Patch applied successfully!")
else:
log("Patch can be applied successfully.")
return True
except Exception as e:
log(f"Failed to apply patch: {e}")
return False

def main():
if len(sys.argv) < 2 or len(sys.argv) > 3:
log("Usage: python apply_runtime_patch.py [--check] <patch_file>")
sys.exit(1)

check_flag = False
if len(sys.argv) == 3:
if sys.argv[1] != "--check":
log("Invalid argument. Use --check for check mode.")
sys.exit(1)
check_flag = True
patch_file = sys.argv[2]
else:
patch_file = sys.argv[1]

# Clear the log file before starting
open(LOG_FILE, "w").close()

success = apply_patch_line_by_line(patch_file, check_flag, hunk_filter=filter_hunk)
sys.exit(0 if success else 1)

if __name__ == "__main__":
main()
137 changes: 137 additions & 0 deletions scripts/create_runtime_patch.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#!/bin/bash
set -e

# Define colors
RED=$(tput setaf 1)
WHITE=$(tput setaf 7)
BLUE=$(tput setaf 4)
RESET=$(tput sgr0)

# Function to print messages with colors
print_message() {
local message=$1
local color=$2
echo "${color}${message}${RESET}"
}

# Check if the correct number of arguments is provided
if [ "$#" -lt 2 ] || [ "$#" -gt 3 ]; then
echo "Usage: $0 <current_paseo_runtime_version> <new_polkadot_runtime_version> [process_parachains]"
echo "process_parachains: Optional. Set to 'true' to process parachains. Defaults to 'false'."
exit 1
fi

# Define list of parachains to copy
# Format: "parachain_name origin_dir dest_dir"
# parachain_name: name of the parachain
# origin_dir: relative path in polkadot_runtime_next/system-parachains/
# dest_dir: relative path in paseo_runtime/system-parachains/
PARACHAINS=(
"asset_hub asset-hubs/asset-hub-polkadot asset-hub-paseo"
)


CURRENT_TAG=$1
NEXT_TAG=$2
PROCESS_PARACHAINS=${3:-false}
POLKADOT_CURRENT_TAG=v${CURRENT_TAG}
POLKADOT_NEXT_TAG=v${NEXT_TAG}


print_message "========================================" "${GREEN}"
print_message "Creating patches from tag ${POLKADOT_CURRENT_TAG} to ${POLKADOT_NEXT_TAG}" "${GREEN}"
print_message "Parachains processing: ${PROCESS_PARACHAINS}" "${GREEN}"
print_message "========================================" "${GREEN}"

rm -rf tmp_runtime
mkdir tmp_runtime
cd tmp_runtime

print_message "----- Cloning repositories -----" "${BLUE}"
git clone --depth 1 https://github.com/paseo-network/runtimes.git paseo_runtime
git clone --depth 1 --branch ${POLKADOT_CURRENT_TAG} https://github.com/polkadot-fellows/runtimes.git polkadot_runtime_current
git clone --depth 1 --branch ${POLKADOT_NEXT_TAG} https://github.com/polkadot-fellows/runtimes.git polkadot_runtime_next

print_message "----- Copying current Polkadot runtime to Paseo -----" "${BLUE}"
cp -fr polkadot_runtime_current/relay/polkadot/* paseo_runtime/relay/paseo/.

print_message "----- Creating temporary branch in Paseo repo -----" "${BLUE}"
cd paseo_runtime
git switch -c tmp/${CURRENT_TAG}-runtime
git add .
git commit -m "Revert to Polkadot ${CURRENT_TAG} runtime"

print_message "----- Reverting changes to keep Paseo-specific modifications -----" "${RED}"
git revert --no-edit HEAD
LATEST_COMMIT=$(git rev-parse HEAD)

print_message "----- Creating new branch for updated runtime -----" "${BLUE}"
git switch main
git switch -c release/${NEXT_TAG}-runtime

print_message "----- Copying new Polkadot runtime to Paseo -----" "${BLUE}"
rm -rf relay/paseo/*
cp -rf ../polkadot_runtime_next/relay/polkadot/* relay/paseo/.
cp -f ../polkadot_runtime_next/Cargo.toml ./

if [ "$PROCESS_PARACHAINS" = "true" ]; then
print_message "----- Copying system-parachains files -----" "${BLUE}"
cp ../polkadot_runtime_next/system-parachains/constants/Cargo.toml system-parachains/constants
cp ../polkadot_runtime_next/system-parachains/constants/src/polkadot.rs system-parachains/constants/src/paseo.rs
cp ../polkadot_runtime_next/system-parachains/constants/src/lib.rs system-parachains/constants/src/

print_message "Copied system-parachains files:" "${WHITE}"
print_message "- Cargo.toml" "${WHITE}"
print_message "- constants/src/paseo.rs (renamed from polkadot.rs)" "${WHITE}"
print_message "- constants/src/lib.rs" "${WHITE}"


print_message "----- Copying specified parachains -----" "${BLUE}"
for parachain in "${PARACHAINS[@]}"; do
read -r parachain_name source_dir dest_dir <<< "$parachain"
source_dir="../polkadot_runtime_next/system-parachains/${source_dir}"
dest_dir="system-parachains/${dest_dir}"
if [ -d "$source_dir" ]; then
mkdir -p "$dest_dir"
cp -rf "$source_dir"/* "$dest_dir/"
print_message "Copied ${parachain_name} from ${source_dir} to ${dest_dir}" "${WHITE}"
else
print_message "Warning: ${source_dir} not found for ${parachain_name}" "${RED}"
fi
done
fi

git add .
git commit -m "Update to Polkadot ${NEXT_TAG} runtime"
if [ "$PROCESS_PARACHAINS" = "true" ]; then
git commit --amend -m "Update to Polkadot ${NEXT_TAG} runtime and copy specified parachains"
fi

print_message "----- Creating patch files for Paseo-specific modifications -----" "${WHITE}"
mkdir -p ../../patches

# Create patch for relay/paseo and Cargo.toml
git diff ${LATEST_COMMIT} HEAD -- relay/paseo Cargo.toml > ../../patches/relay_polkadot.patch

if [ "$PROCESS_PARACHAINS" = "true" ]; then
# Create patches for each parachain
for parachain in "${PARACHAINS[@]}"; do
read -r parachain_name _ dest_dir <<< "$parachain"
parachain_dir="system-parachains/${dest_dir}"
if [ -d "$parachain_dir" ]; then
git diff ${LATEST_COMMIT} HEAD -- "$parachain_dir" > "../../patches/parachain_${parachain_name}.patch"
print_message "Created patch for ${parachain_name}" "${WHITE}"
else
print_message "Warning: ${dest_dir} not found for ${parachain_name}, skipping patch creation" "${RED}"
fi
done

# Create patch for system-parachains/constants and system-parachains/Cargo.toml
git diff ${LATEST_COMMIT} HEAD -- system-parachains/constants system-parachains/constants/Cargo.toml > "../../patches/system_parachains_common.patch"
print_message "Created patch for system-parachains/constants"
fi

print_message "--------------------" "${BLUE}"
print_message "----- Patch files created in patches/ directory -----" "${WHITE}"
print_message "----- Apply these patch files to integrate Paseo-specific changes -----" "${WHITE}"
print_message "--------------------" "${BLUE}"
20 changes: 20 additions & 0 deletions scripts/replacements_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"replacements": {
"[\"Polkadot Fellowship\"]": "[\"Paseo Core Team\"]",
"https://github.com/polkadot-fellows/runtimes.git": "https://github.com/paseo-network/runtimes.git",
"polkadot_runtime_constants": "paseo_runtime_constants",
"polkadot-runtime-constants": "paseo-runtime-constants",
"runtime::polkadot": "runtime::paseo",
"polkadot-runtime\"":"paseo-runtime\"",
"spec_name: create_runtime_str!(\"polkadot\"),": "spec_name: create_runtime_str!(\"paseo\"),",
"impl_name: create_runtime_str!(\"parity-polkadot\"),": "impl_name: create_runtime_str!(\"paseo-testnet\"),",
"type LeaseOffset = LeaseOffset;": "type LeaseOffset = ();",
"// Polkadot version identifier;": "// Portico version identifier;",
"/// Runtime version (Polkadot).": "/// Runtime version (Paseo).",
"//! XCM configuration for Polkadot.": "//! XCM configuration for Paseo.",
"pub const EPOCH_DURATION_IN_SLOTS: BlockNumber = prod_or_fast!(4 * HOURS, 1 * MINUTES);":"pub const EPOCH_DURATION_IN_SLOTS: BlockNumber = prod_or_fast!(1 * HOURS, 1 * MINUTES);",
"Polkadot": "Paseo",
"polkadot": "paseo"
},
"remove_block_pattern": "\\s*// Polkadot Genesis was on May 26, 2020\\..*?DOT_LEASE_OFFSET\\\"\\);"
}
Loading

0 comments on commit eb05342

Please sign in to comment.