Created
August 30, 2026 18:18
-
-
Save dltj/f53399de3e441f76e6d737a32c491485 to your computer and use it in GitHub Desktop.
VuFind 3-Way Merge
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env -S uv run --script | |
| # Note: This script requires 'uv' (https://docs.astral.sh/uv/). | |
| # Install: curl -LsSf https://astral.sh/uv/install.sh | sh (or `brew install uv`) | |
| # Execution: ./script.py OR uv run script.py | |
| # /// script | |
| # requires-python = ">=3.11" | |
| # dependencies = [ | |
| # "click", | |
| # ] | |
| # /// | |
| import os | |
| import shutil | |
| import subprocess | |
| from pathlib import Path | |
| from typing import Iterator | |
| import click | |
| DIFF_EXECUTABLE = os.getenv("DIFF_EXECUTABLE") or shutil.which("kdiff3") or "kdiff3" | |
| def iter_files(directory: Path) -> Iterator[Path]: | |
| """Recursively yields file paths from a directory.""" | |
| return (path for path in directory.rglob("*") if path.is_file()) | |
| @click.command() | |
| @click.argument("site_dir", type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path)) | |
| @click.argument("base_dir", type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path)) | |
| @click.argument("target_dir", type=click.Path(exists=True, file_okay=False, dir_okay=True, path_type=Path)) | |
| def threeway_merge(site_dir: Path, base_dir: Path, target_dir: Path) -> None: | |
| """Perform a three-way merge of a site config against changed VuFind versions.""" | |
| for site_file_path in iter_files(site_dir): | |
| relative_path = site_file_path.relative_to(site_dir) | |
| base_file_path = base_dir / relative_path | |
| target_file_path = target_dir / relative_path | |
| print(f"{site_file_path=} {base_file_path=} {target_file_path=}") | |
| try: | |
| result = subprocess.run( | |
| [ | |
| DIFF_EXECUTABLE, | |
| base_file_path, | |
| site_file_path, | |
| target_file_path, | |
| "-o", | |
| site_file_path, | |
| ], | |
| check=True, # Raise CalledProcessError if the command exits with a non-zero status | |
| text=True, # Capture stdout and stderr as strings | |
| capture_output=True, # Capture stdout and stderr | |
| ) | |
| print("Output:", result.stdout) | |
| print("Error:", result.stderr) | |
| except subprocess.CalledProcessError as e: | |
| print(f"Command '{' '.join(map(str, e.cmd))}' exited with code {e.returncode}.") | |
| print(f"Error output: {e.stderr}") | |
| if __name__ == "__main__": | |
| threeway_merge() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment