feat(cf-validate): проверка 10 — тела конфигурации Ext/*.xml в версии Configuration.xml

Конфигурация для платформы — такой же объект, как остальные: Configuration.xml и его штампованные
тела Ext/*.xml (CommandInterface, MainSectionCommandInterface, HomePageWorkArea, Splash,
MainSectionPicture, Logo) загружаются только в одной версии формата — иначе «Версия формата
загружаемого файла … отличается от версии формата ранее загруженных файлов». Проверено на
8.3.27.1859: расхождение любого из этих файлов с Configuration.xml — отказ; Configuration.xml вместе
со всеми своими телами в другой версии, чем объекты, — грузится. Сквозного обхода выгрузки нет:
на ERP это 74 тыс. файлов, а платформа при загрузке и так называет файл.

Co-Authored-By: Sergei Pleshanov <2357qwr@gmail.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Nick Shirokov
2026-09-26 22:07:54 +03:00
co-authored by Sergei Pleshanov Claude Opus 5.5
parent c943acefa2
commit 5a62b37017
13 changed files with 683 additions and 3 deletions
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
# cf-validate v1.10 — Validate 1C configuration XML structure
# cf-validate v1.11 — Validate 1C configuration XML structure
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
"""Validates Configuration.xml: root structure, InternalInfo, properties, ChildObjects, languages."""
import sys, os, argparse, re
@@ -148,6 +148,22 @@ def format_rank(ver):
return int(m.group(1)) * 100 + int(m.group(2)) if m else 0
# Штамп версии формата — атрибут version КОРНЕВОГО элемента файла. Копия общего эталона (семья
# root_version, авторитет — meta-validate).
def root_version(xml_path):
if not os.path.isfile(xml_path):
return None
with open(xml_path, "rb") as f:
head = f.read(4096).decode("utf-8", errors="ignore")
m = re.search(r'<[A-Za-z_][\w.:-]*(\s[^>]*)?/?>', head)
if not m:
return None
v = re.search(r'(?:^|\s)version="([^"]*)"', m.group(1) or "")
if v:
return v.group(1)
return None
class Reporter:
def __init__(self, max_errors, detailed=False):
self.errors = 0
@@ -636,6 +652,29 @@ def main():
for err in form_ref_errors:
r.error(f'9. {err}')
# --- Check 10: тела конфигурации Ext/*.xml в версии Configuration.xml ---
# Конфигурация для платформы — такой же объект, как остальные: Configuration.xml и штампованные тела
# Ext/*.xml (CommandInterface, HomePageWorkArea, Splash, …) загружаются только в одной версии формата
# («Версия формата загружаемого файла … отличается от версии формата ранее загруженных файлов»).
cfg_ext_dir = os.path.join(config_dir, 'Ext')
cfg_bodies_ok = 0
cfg_body_errors = 0
if version and os.path.isdir(cfg_ext_dir):
body_names = sorted(n for n in os.listdir(cfg_ext_dir)
if n.lower().endswith('.xml') and os.path.isfile(os.path.join(cfg_ext_dir, n)))
for bn in body_names:
body_ver = root_version(os.path.join(cfg_ext_dir, bn))
if not body_ver:
continue
if body_ver == version:
cfg_bodies_ok += 1
continue
cfg_body_errors += 1
r.error(f'10. Ext/{bn} is stamped {body_ver}, Configuration.xml {version} '
'— the platform refuses to load parts of one object in different formats')
if cfg_body_errors == 0 and cfg_bodies_ok > 0:
r.ok(f'10. Format version: {cfg_bodies_ok} stamped Ext part(s) agree with Configuration.xml')
# --- Final output ---
r.finalize(out_file)
sys.exit(1 if r.errors > 0 else 0)