wickbot / scripts /package_mt5.py
Joedroid's picture
deploy: update wickbot codebase (3960 files) (part 3)
9a8c002 verified
Raw
History Blame Contribute Delete
5.58 kB
"""
Package MT5 terminal files from a local Windows installation into mt5_terminal/
for Docker/Wine deployment on Hugging Face Spaces.
Usage — merge from multiple sources (Program Files + AppData):
uv run python scripts/package_mt5.py \\
--source "C:\Program Files\MetaTrader 5" \\
--source "$env:APPDATA\MetaQuotes\Terminal\{GUID}"
Later sources override earlier ones for the same relative path.
"""
import argparse
import shutil
import sys
from pathlib import Path
ESSENTIAL_FILES = [
"terminal64.exe",
"metaeditor64.exe",
]
ESSENTIAL_DIRS = [
"MQL5",
"history",
"profiles",
]
EXCLUDE_PATTERNS = [
"*.log",
"MQL5/Logs",
"MQL5/Experts/Examples",
"MQL5/Experts/Samples",
"MQL5/Indicators/Examples",
"MQL5/Indicators/Samples",
"MQL5/Scripts/Examples",
"MQL5/Scripts/Samples",
"MQL5/Include/Examples",
"history/*.hst",
"profiles/*.chr",
]
def should_exclude(rel_path: Path, excludes: list[str]) -> bool:
rel_str = str(rel_path)
for pattern in excludes:
if pattern in rel_str:
return True
for parent in rel_path.parents:
if parent.name in ["Logs", "Examples", "Samples"]:
return True
return False
def package_source(source: Path, dest: Path) -> tuple[int, int]:
"""Copy files from one source into dest (merge, don't clear dest)."""
if not source.exists():
print(f" [!] Source not found: {source}")
return 0, 0
files_copied = 0
bytes_copied = 0
for fname in ESSENTIAL_FILES:
src_file = source / fname
if src_file.exists():
dst_file = dest / fname
size = src_file.stat().st_size
shutil.copy2(src_file, dst_file)
files_copied += 1
bytes_copied += size
print(f" + {fname} ({size / 1024 / 1024:.1f} MB)")
for dirname in ESSENTIAL_DIRS:
src_dir = source / dirname
if not src_dir.exists():
continue
print(f" Copying {dirname}/...")
for src_path in src_dir.rglob("*"):
if not src_path.is_file():
continue
rel_path = src_path.relative_to(source)
if should_exclude(rel_path, EXCLUDE_PATTERNS):
continue
dst_path = dest / rel_path
dst_path.parent.mkdir(parents=True, exist_ok=True)
existing_size = dst_path.stat().st_size if dst_path.exists() else 0
shutil.copy2(src_path, dst_path)
files_copied += 1
bytes_copied += src_path.stat().st_size
return files_copied, bytes_copied
def main():
parser = argparse.ArgumentParser(description="Package MT5 terminal for Docker deployment")
parser.add_argument("--source", type=str, action="append", required=True,
help="Path to MT5 source (use multiple times to merge: "
"--source 'C:\\Program Files\\MetaTrader 5' "
"--source '$APPDATA\\MetaQuotes\\Terminal\\...')")
parser.add_argument("--dest", type=str, default="mt5_terminal",
help="Destination directory (default: mt5_terminal/)")
parser.add_argument("--dry-run", action="store_true",
help="Show what would be copied without actually copying")
args = parser.parse_args()
dest = Path(args.dest)
sources = [Path(s) for s in args.source]
print(f"Packaging MT5 from {len(sources)} source(s):")
for s in sources:
print(f" => {s}")
print(f"Destination: {dest}")
print()
if args.dry_run:
print("DRY RUN - no files will be copied")
total_files = 0
total_bytes = 0
for source in sources:
if not source.exists():
print(f" [!] Not found: {source}")
continue
for fname in ESSENTIAL_FILES:
f = source / fname
if f.exists():
size = f.stat().st_size
print(f" + {fname} ({size / 1024 / 1024:.1f} MB)")
total_files += 1
total_bytes += size
for dirname in ESSENTIAL_DIRS:
d = source / dirname
if d.exists():
count = sum(
1 for p in d.rglob("*")
if p.is_file() and not should_exclude(p.relative_to(source), EXCLUDE_PATTERNS)
)
print(f" + {dirname}/ (~{count} files)")
total_files += count
total_bytes += sum(
p.stat().st_size for p in d.rglob("*")
if p.is_file() and not should_exclude(p.relative_to(source), EXCLUDE_PATTERNS)
)
print(f"\nWould copy: ~{total_files} files, {total_bytes / 1024 / 1024:.1f} MB total")
return
dest.mkdir(parents=True, exist_ok=True)
total_files = 0
total_bytes = 0
print("Copying files...")
for i, source in enumerate(sources, 1):
print(f"\n[{i}/{len(sources)}] {source}")
fc, bc = package_source(source, dest)
total_files += fc
total_bytes += bc
print(f"\n[OK] Packaged {total_files} files ({total_bytes / 1024 / 1024:.1f} MB)")
print(f"\nNext steps:")
print(f" 1. Review {dest}/ to ensure it looks correct")
print(f" 2. Check size: du -sh {dest}/")
print(f" 3. Deploy to HF: uv run python scripts/upload_to_hf.py --mt5")
if __name__ == "__main__":
main()