mirror of
https://github.com/Nikolay-Shirokov/cc-1c-skills.git
synced 2026-09-30 14:05:53 +03:00
feat(cfe-validate): проверка 17 — версия формата согласована внутри объектов расширения
Объекты расширения платформа загружает по тому же правилу, что и объекты конфигурации: дескриптор и его штампованные тела (у заимствованной формы — ещё и вложенный <BaseForm version=…>) только в одной версии формата, иначе «Версия формата загружаемого файла … отличается от версии формата ранее загруженных файлов». Проверено загрузкой расширения на 8.3.27.1859: тело формы или <BaseForm> не в версии дескриптора — отказ; заимствованный объект целиком в 2.18 в расширении 2.20 загружается. Первое — ошибка, второе — предупреждение о неоднородной выгрузке. Так же сверяются Configuration.xml расширения и его Ext/*.xml. 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:
co-authored by
Sergei Pleshanov
Claude Opus 5.5
parent
fbcf65e12d
commit
88eca0b000
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
# cfe-validate v1.17 — Validate 1C configuration extension XML structure (CFE)
|
||||
# cfe-validate v1.18 — Validate 1C configuration extension XML structure (CFE)
|
||||
# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills
|
||||
"""Validates extension Configuration.xml: root, InternalInfo, extension properties, ChildObjects, borrowed objects."""
|
||||
import sys, os, argparse, re
|
||||
@@ -235,6 +235,22 @@ def format_rank(ver):
|
||||
return int(m.group(1)) * 100 + int(m.group(2)) if m else 0
|
||||
|
||||
|
||||
# Штамп версии формата — атрибут version КОРНЕВОГО элемента файла. Копия общего эталона (семья
|
||||
# root_version, авторитет — meta-validate). Корневого: в Form.xml расширения ниже стоит <BaseForm version=…>.
|
||||
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
|
||||
@@ -1302,6 +1318,96 @@ def main():
|
||||
for issue in state_issues:
|
||||
r.warn(f'16. {issue}')
|
||||
|
||||
# --- Check 17: версия формата согласована внутри объектов расширения; сверка с Configuration.xml ---
|
||||
# Дескриптор объекта и его штампованные тела X/Ext/*.xml, дескриптор формы/макета и его тело (и
|
||||
# Configuration.xml с его Ext/*.xml) платформа загружает только в одной версии формата: «Версия формата
|
||||
# загружаемого файла … отличается от версии формата ранее загруженных файлов». Объект целиком в другой
|
||||
# версии, чем расширение, загружается — это лишь неоднородность выгрузки (типично после мержа веток,
|
||||
# выгруженных разными платформами), поэтому такое расхождение только предупреждение.
|
||||
if version:
|
||||
ver_descriptors = [resolved_path]
|
||||
if child_obj_node is not None:
|
||||
for child in child_obj_node:
|
||||
if not isinstance(child.tag, str):
|
||||
continue
|
||||
type_name = etree.QName(child.tag).localname
|
||||
if type_name not in CHILD_TYPE_DIR_MAP:
|
||||
continue
|
||||
ver_obj_name = (child.text or '').strip()
|
||||
if not ver_obj_name:
|
||||
continue
|
||||
ver_type_dir = os.path.join(config_dir, CHILD_TYPE_DIR_MAP[type_name])
|
||||
ver_obj_file = os.path.join(ver_type_dir, f'{ver_obj_name}.xml')
|
||||
if not os.path.isfile(ver_obj_file):
|
||||
continue
|
||||
ver_descriptors.append(ver_obj_file)
|
||||
for sub in ('Forms', 'Templates'):
|
||||
sub_dir = os.path.join(ver_type_dir, ver_obj_name, sub)
|
||||
if not os.path.isdir(sub_dir):
|
||||
continue
|
||||
names = sorted(n for n in os.listdir(sub_dir)
|
||||
if n.lower().endswith('.xml') and os.path.isfile(os.path.join(sub_dir, n)))
|
||||
for n in names:
|
||||
ver_descriptors.append(os.path.join(sub_dir, n))
|
||||
|
||||
def ver_rel(p):
|
||||
return p[len(config_dir):].lstrip('\\/').replace('\\', '/')
|
||||
|
||||
ver_errors = 0
|
||||
ver_bodies_ok = 0
|
||||
ver_off = []
|
||||
for desc in ver_descriptors:
|
||||
if r.stopped:
|
||||
break
|
||||
is_root = desc == resolved_path
|
||||
desc_ver = version if is_root else root_version(desc)
|
||||
if not desc_ver:
|
||||
continue
|
||||
if not is_root and desc_ver != version:
|
||||
ver_off.append(f'{ver_rel(desc)} {desc_ver}')
|
||||
# Тела Configuration.xml лежат в <корень>/Ext, тела объекта X.xml — в X/Ext.
|
||||
if is_root:
|
||||
ext_dir = os.path.join(config_dir, 'Ext')
|
||||
else:
|
||||
ext_dir = os.path.join(os.path.dirname(desc), os.path.splitext(os.path.basename(desc))[0], 'Ext')
|
||||
if not os.path.isdir(ext_dir):
|
||||
continue
|
||||
body_names = sorted(n for n in os.listdir(ext_dir)
|
||||
if n.lower().endswith('.xml') and os.path.isfile(os.path.join(ext_dir, n)))
|
||||
for bn in body_names:
|
||||
body = os.path.join(ext_dir, bn)
|
||||
body_ver = root_version(body)
|
||||
if not body_ver:
|
||||
continue
|
||||
# У заимствованной формы второй штамп — <BaseForm version=…> внутри тела; платформа сверяет
|
||||
# с дескриптором и его.
|
||||
if bn == 'Form.xml':
|
||||
with open(body, 'r', encoding='utf-8-sig') as f:
|
||||
body_text = f.read()
|
||||
bfm = re.search(r'<BaseForm\s[^>]*?version="([^"]*)"', body_text)
|
||||
if bfm and bfm.group(1) != desc_ver:
|
||||
ver_errors += 1
|
||||
r.error(f'17. {ver_rel(body)} <BaseForm> is stamped {bfm.group(1)}, its descriptor '
|
||||
f'{ver_rel(desc)} {desc_ver} — the platform refuses to load parts of one object in different formats')
|
||||
if r.stopped:
|
||||
break
|
||||
if body_ver == desc_ver:
|
||||
ver_bodies_ok += 1
|
||||
continue
|
||||
ver_errors += 1
|
||||
r.error(f'17. {ver_rel(body)} is stamped {body_ver}, its descriptor {ver_rel(desc)} {desc_ver} '
|
||||
'— the platform refuses to load parts of one object in different formats')
|
||||
if r.stopped:
|
||||
break
|
||||
if ver_off:
|
||||
shown = ', '.join(ver_off[:5])
|
||||
if len(ver_off) > 5:
|
||||
shown += f', … (+{len(ver_off) - 5})'
|
||||
r.warn(f'17. Format version differs from the extension ({version}): {shown} — the platform loads it, '
|
||||
'but the dump is no longer uniform (typical after merging branches dumped by different platforms)')
|
||||
elif ver_errors == 0 and ver_bodies_ok > 0:
|
||||
r.ok(f'17. Format version: {ver_bodies_ok} stamped part(s) agree with their descriptors and the extension')
|
||||
|
||||
# --- Breadcrumb: controlled methods (&ИзменениеИКонтроль) drift is not checked here ---
|
||||
ctrl_kw = bsl_keywords()
|
||||
ctrl_re = re.compile(r'^\s*&(?:' + ctrl_kw["ru"]["Control"] + '|' + ctrl_kw["en"]["Control"] + r')\(', re.IGNORECASE)
|
||||
|
||||
Reference in New Issue
Block a user