diff --git a/.gitignore b/.gitignore index 94a6e95..945184d 100644 --- a/.gitignore +++ b/.gitignore @@ -38,4 +38,5 @@ runtime/wasm/target/ substrate.code-workspace target/ **/tmp_runtime -**/tmp_hub \ No newline at end of file +**/tmp_hub +*.log \ No newline at end of file diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..f7be67b --- /dev/null +++ b/scripts/README.md @@ -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 [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_.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] + +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 +``` + +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. diff --git a/scripts/apply_runtime_patch.py b/scripts/apply_runtime_patch.py new file mode 100644 index 0000000..659ea72 --- /dev/null +++ b/scripts/apply_runtime_patch.py @@ -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] ") + 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() \ No newline at end of file diff --git a/scripts/create_runtime_patch.sh b/scripts/create_runtime_patch.sh new file mode 100755 index 0000000..39a4420 --- /dev/null +++ b/scripts/create_runtime_patch.sh @@ -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 [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}" \ No newline at end of file diff --git a/scripts/replacements_config.json b/scripts/replacements_config.json new file mode 100644 index 0000000..5e65d76 --- /dev/null +++ b/scripts/replacements_config.json @@ -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\\\"\\);" + } \ No newline at end of file diff --git a/scripts/revert_unwanted_changes.py b/scripts/revert_unwanted_changes.py new file mode 100644 index 0000000..ed9c3b8 --- /dev/null +++ b/scripts/revert_unwanted_changes.py @@ -0,0 +1,113 @@ +import subprocess +import re +import os +import sys +import json + +# Global variable for the log file name +LOG_FILE = "revert_unwanted_changes.log" + +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 get_changed_files(): + """Get the list of changed *.rs and *.toml files using git.""" + result = subprocess.run(['git', 'diff', '--name-only', '*.rs', '*.toml'], capture_output=True, text=True, check=True) + files = result.stdout.strip().split('\n') + log(f"Changed .rs and .toml files: {files}") + return [f for f in files if f.strip()] # Remove empty strings + +def get_file_diff(file_path): + """Get the diff for a specific file.""" + result = subprocess.run(['git', 'diff', file_path], capture_output=True, text=True, check=True) + return result.stdout + +def load_replacements(replacements_file): + """Load replacements and other configurations from the JSON configuration file.""" + with open(replacements_file, 'r') as f: + config = json.load(f) + return config.get("replacements", {}), config.get("remove_block_pattern", "") + +def apply_filters(line, replacements): + """Apply filters to determine if a line should be reverted.""" + for pattern, replacement in replacements.items(): + # Use word boundaries to match exact words + line = re.sub(rf'\b{re.escape(pattern)}\b', replacement, line) + return line + +def remove_text_block(file_path, pattern): + """Remove text blocks matching the pattern from the file.""" + with open(file_path, 'r') as file: + content = file.read() + content = re.sub(pattern, "", content, flags=re.DOTALL) + with open(file_path, 'w') as file: + file.write(content) + log(f"Removed text blocks matching pattern from {file_path}") + +def revert_changes(file_path, diff, replacements): + """Revert unwanted changes in a file based on its diff.""" + if not os.path.exists(file_path): + log(f"File {file_path} does not exist, skipping.") + return + + with open(file_path, 'r') as f: + content = f.readlines() + + changes_made = False + lines_to_revert = [] + for line in diff.split('\n'): + if line.startswith('+') and not line.startswith('+++'): + # This is an added line + revert_line = apply_filters(line[1:], replacements) + if revert_line != line[1:]: + lines_to_revert.append((line[1:], revert_line)) + + for i, line in enumerate(content): + for original, reverted in lines_to_revert: + if line.strip() == original.strip(): + content[i] = reverted + '\n' + log(f"Reverted in {file_path}, line {i+1}: {original.strip()} -> {reverted.strip()}") + changes_made = True + + if changes_made: + with open(file_path, 'w') as f: + f.writelines(content) + log(f"Changes reverted in {file_path}") + else: + log(f"No changes to revert in {file_path}") + +def main(): + if len(sys.argv) != 2: + print("Usage: python revert_unwanted_changes.py ") + sys.exit(1) + + replacements_file = sys.argv[1] + + # Clear the log file before starting + open(LOG_FILE, "w").close() + + log("Starting revert_unwanted_changes.py") + + try: + replacements, remove_block_pattern = load_replacements(replacements_file) + changed_files = get_changed_files() + + for file_path in changed_files: + log(f"Processing {file_path}") + diff = get_file_diff(file_path) + revert_changes(file_path, diff, replacements) + if remove_block_pattern: + remove_text_block(file_path, remove_block_pattern) + + log("Finished processing all changed files.") + except Exception as e: + log(f"An error occurred: {str(e)}") + sys.exit(1) + + log("revert_unwanted_changes.py completed successfully.") + +if __name__ == "__main__": + main() \ No newline at end of file