mt5-manager/docs/articles/part-04/publish/build_archive.py
86 lines
4.6 KiB
Python
'''Собирает MQL5.zip для четвёртой части из тега p4.
Исходник сервиса берётся из рабочего дерева только после проверки, что от p4
отличаются лишь комментарии, пробелы и версия. Личные файлы не читаются.
Запуск: python docs/articles/part-04/publish/build_archive.py
'''
import json
import re
import subprocess
from pathlib import Path
from zipfile import ZIP_DEFLATED, ZipFile
ROOT = Path(__file__).resolve().parents[4]
OUTPUT = Path(__file__).resolve().parent / 'MQL5.zip'
PREFIX = 'MQL5/Shared Projects/mt5-manager/'
BASE = 'p4'
def git(*args):
return subprocess.run(['git', *args], cwd=ROOT, capture_output=True, check=True).stdout
def executable(text):
text = re.sub(r'^#property version[^\n]*', '', text, flags=re.M)
parts = re.compile(r'"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'|//[^\n]*|/\*[\s\S]*?\*/')
text = parts.sub(lambda m: '' if m.group().startswith(('//', '/*')) else m.group(), text)
return re.findall(r'"(?:\\.|[^"\\])*"|\'(?:\\.|[^\'\\])*\'|\w+|[^\s]', text)
def build():
baseline = git('show', f'{BASE}:ServiceStateWriter.mq5').decode('utf-8-sig')
service = (ROOT / 'ServiceStateWriter.mq5').read_text(encoding='utf-8-sig')
if executable(baseline) != executable(service):
raise RuntimeError('Сервис отличается от p4 по логике. Для архива четвёртой части нужен её исходник.')
paths = git('ls-tree', '-r', '--name-only', BASE).decode().splitlines()
paths = [name for name in paths if name in {
'LICENSE', 'README.md', 'ServiceStateWriter.mq5', 'config.json', 'config.py',
'deploy_service.py', 'extracticon.py', 'main.py', 'mt5_control.py', 'run.py'}
or name.startswith(('static/', 'templates/'))]
files = {name: git('show', f'{BASE}:{name}') for name in paths}
files['ServiceStateWriter.mq5'] = service.encode('utf-8')
config = json.loads(files['config.json'].decode('utf-8-sig'))
config['mt5_folder'] = 'C:/MT5'
config['database'] = 'C:/Users/USERNAME/AppData/Roaming/MetaQuotes/Terminal/Common/Files/mt5-manager.sqlite'
for name, record in config['terminals'].items():
record['login'] = ''
record['server'] = ''
record.pop('password', None)
record.pop('path', None)
files['config.json'] = (json.dumps(config, ensure_ascii=False, indent=2) + '\n').encode('utf-8')
note = '''# Архив к четвёртой части
Распакуйте MQL5.zip в папку данных терминала. Проект появится в
MQL5/Shared Projects/mt5-manager. Для portable-режима это папка установки MT5.
Перед запуском исправьте database в config.json: замените USERNAME на имя
пользователя Windows. Проверьте mt5_folder и имена уже установленных терминалов.
Логины и серверы в примере оставлены пустыми; используются счета, сохранённые MT5.
Откройте ServiceStateWriter.mq5 в MetaEditor и скомпилируйте его. Полученный EX5
будет расположен в MQL5/Services/Shared Projects/mt5-manager. Скопируйте EX5 по
этому же пути в остальные терминалы и static/services.ini в их папки config.
Либо используйте deploy_service.py, как описано в статье.
Архив содержит код версии p4 с исправленным оформлением сервиса 1.01.
Возможности пятой части в него не включены. Исполняемые EX5 не распространяются.
---
'''
files['README.md'] = note.encode('utf-8') + files['README.md']
with ZipFile(OUTPUT, 'w', compression=ZIP_DEFLATED) as archive:
for name, data in sorted(files.items()):
if name.lower().endswith('.ex5'):
raise RuntimeError('EX5 запрещён в архиве публикации.')
archive.writestr(PREFIX + name, data)
with ZipFile(OUTPUT) as archive:
assert archive.testzip() is None
assert all(name.startswith('MQL5/') for name in archive.namelist())
assert not any(name.lower().endswith('.ex5') for name in archive.namelist())
print(f'MQL5.zip: {len(files)} файлов, {OUTPUT.stat().st_size} байт; база p4, без EX5 и личных реквизитов.')
if __name__ == '__main__':
build()