diff --git a/CHANGELOG.md b/CHANGELOG.md
index e235a3d..f9ff2ae 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
All notable changes to **export_1c_help** are documented in this file.
+## [0.3.4] - 2026-07-24
+
+### Added
+
+- Page footer / Home / History include configuration **display name** (Synonym) and **vendor** (Vendor) from `Configuration.xml`
+
## [0.3.3] - 2026-07-23
### Fixed
diff --git a/README.md b/README.md
index 7c5b9ec..b547008 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# export_1c_help
-**Версия:** см. [`VERSION`](VERSION) (текущая: **0.3.0**) · [CHANGELOG](CHANGELOG.md)
+**Версия:** см. [`VERSION`](VERSION) (текущая: **0.3.4**) · [CHANGELOG](CHANGELOG.md)
**Лицензия:** [MIT](LICENSE)
Выгрузка встроенной справки **любой** конфигурации 1С (`**/Ext/Help/ru.html`) в Markdown и публикация в wiki git-репозитория (Gitea / GitLab: `*.wiki.git`).
@@ -124,8 +124,8 @@ python3 export_1c_help.py push -c /path/to/config
| Где | Что |
|-----|-----|
-| `Home.md` | версия конфигурации **этой** выгрузки |
-| подвал страницы | версия выгрузки; **создана в** / **изменена в** |
+| `Home.md` | наименование, версия, разработчик этой выгрузки |
+| подвал страницы | конфигурация, версия, разработчик; **создана в** / **изменена в** |
| `History.md` | сводка +/-/~ и удалённые (`deleted_in`) |
| `manifest.json` | lifecycle + `content_hash` + `exports[]` |
diff --git a/VERSION b/VERSION
index 1c09c74..42045ac 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.3.3
+0.3.4
diff --git a/export1c_help/config_version.py b/export1c_help/config_version.py
index 3952e33..bc9cc91 100644
--- a/export1c_help/config_version.py
+++ b/export1c_help/config_version.py
@@ -1,16 +1,34 @@
-"""Read 1C configuration version from dump tree."""
+"""Read 1C configuration identity from dump tree."""
from __future__ import annotations
import re
+from dataclasses import dataclass
from pathlib import Path
_VERSION_TAG = re.compile(r"([^<]+)")
-_NAME_TAG = re.compile(
- r"([^<]+)",
+_NAME_TAG = re.compile(r"([^<]+)")
+_VENDOR_TAG = re.compile(r"([^<]*)")
+_SYN_RE = re.compile(
+ r"\s*\s*ru\s*(.*?)",
+ re.DOTALL,
)
+@dataclass(frozen=True)
+class ConfigIdentity:
+ """Configuration name / version / vendor for wiki footers."""
+
+ version: str
+ name: str | None = None # technical Name
+ synonym: str | None = None # Synonym ru
+ vendor: str | None = None
+
+ @property
+ def display_name(self) -> str | None:
+ return self.synonym or self.name
+
+
def config_root_from_src(src_root: Path) -> Path:
"""src/ → configuration root (parent that usually has VERSION + Configuration.xml)."""
src_root = Path(src_root).resolve()
@@ -19,46 +37,89 @@ def config_root_from_src(src_root: Path) -> Path:
return src_root
+def _configuration_xml(src_root: Path) -> Path | None:
+ src_root = Path(src_root)
+ root = config_root_from_src(src_root)
+ for xml_path in (
+ root / "src" / "Configuration.xml",
+ src_root / "Configuration.xml",
+ root / "Configuration.xml",
+ ):
+ if xml_path.is_file():
+ return xml_path
+ return None
+
+
+def _xml_unescape(s: str) -> str:
+ return (
+ s.replace("<", "<")
+ .replace(">", ">")
+ .replace("&", "&")
+ .replace(""", '"')
+ .replace("'", "'")
+ )
+
+
def read_config_version(src_root: Path) -> str:
"""
Resolve configuration version.
Priority:
1. /VERSION
- 2. /src/Configuration.xml →
- 3. /Configuration.xml →
+ 2. Configuration.xml →
"""
+ return read_config_identity(src_root).version
+
+
+def read_config_name(src_root: Path) -> str | None:
+ """Technical configuration Name from Configuration.xml."""
+ return read_config_identity(src_root).name
+
+
+def read_config_identity(src_root: Path) -> ConfigIdentity:
+ """Version, Name, Synonym (ru), Vendor from dump."""
src_root = Path(src_root)
root = config_root_from_src(src_root)
+
+ version = "unknown"
version_file = root / "VERSION"
if version_file.is_file():
text = version_file.read_text(encoding="utf-8-sig", errors="ignore").strip()
if text:
- return text.splitlines()[0].strip()
+ version = text.splitlines()[0].strip()
- for xml_path in (
- root / "src" / "Configuration.xml",
- src_root / "Configuration.xml",
- root / "Configuration.xml",
- ):
- if not xml_path.is_file():
- continue
+ name: str | None = None
+ synonym: str | None = None
+ vendor: str | None = None
+
+ xml_path = _configuration_xml(src_root)
+ if xml_path is not None:
raw = xml_path.read_text(encoding="utf-8-sig", errors="ignore")
- m = _VERSION_TAG.search(raw)
+ # Prefer Properties block to avoid accidental matches deeper in the file
+ props = raw
+ if "" in raw and "" in raw:
+ props = raw.split("", 1)[1].split("", 1)[0]
+
+ if version == "unknown":
+ m = _VERSION_TAG.search(props)
+ if m:
+ version = m.group(1).strip()
+
+ m = _NAME_TAG.search(props)
if m:
- return m.group(1).strip()
+ name = m.group(1).strip() or None
- return "unknown"
+ m = _SYN_RE.search(props)
+ if m:
+ synonym = _xml_unescape(m.group(1).strip()) or None
+ m = _VENDOR_TAG.search(props)
+ if m:
+ vendor = _xml_unescape(m.group(1).strip()) or None
-def read_config_name(src_root: Path) -> str | None:
- src_root = Path(src_root)
- root = config_root_from_src(src_root)
- xml_path = root / "src" / "Configuration.xml"
- if not xml_path.is_file():
- xml_path = src_root / "Configuration.xml"
- if not xml_path.is_file():
- return None
- raw = xml_path.read_text(encoding="utf-8-sig", errors="ignore")
- m = _NAME_TAG.search(raw)
- return m.group(1).strip() if m else None
+ return ConfigIdentity(
+ version=version or "unknown",
+ name=name,
+ synonym=synonym,
+ vendor=vendor,
+ )
diff --git a/export1c_help/export.py b/export1c_help/export.py
index 92c08fe..45aa141 100644
--- a/export1c_help/export.py
+++ b/export1c_help/export.py
@@ -10,7 +10,7 @@ from datetime import datetime, timezone
from pathlib import Path
from export1c_help.__version__ import __version__
-from export1c_help.config_version import read_config_name, read_config_version
+from export1c_help.config_version import ConfigIdentity, read_config_identity
from export1c_help.convert import default_link_rewrite, extract_h1, html_to_markdown
from export1c_help.discover import HelpPage, build_link_title_map, discover_help_pages
from export1c_help.lifecycle import (
@@ -51,12 +51,15 @@ def re_sub_md(md: str) -> str:
return t
-def _page_footer(rec: PageRecord, *, config_version: str) -> str:
+def _page_footer(rec: PageRecord, *, identity: ConfigIdentity) -> str:
+ display = identity.display_name or "—"
+ vendor = identity.vendor or "—"
lines = [
"",
"---",
"",
- f"*Конфигурация выгрузки: **{config_version}*** ",
+ f"*Конфигурация: **{display}*** ",
+ f"*Версия: **{identity.version}** · разработчик: **{vendor}*** ",
f"*Страница создана в: **{rec.created_in}** · изменена в: **{rec.edited_in}*** ",
f"*Мета: `{rec.meta_key}` · файл: `{rec.source}`*",
"",
@@ -69,21 +72,21 @@ def build_home(
titles: dict[str, str],
*,
source_label: str,
- config_version: str,
- config_name: str | None,
+ identity: ConfigIdentity,
life: LifecycleResult,
) -> str:
by_type: dict[str, list[HelpPage]] = {}
for p in pages:
by_type.setdefault(p.type_dir, []).append(p)
- name_line = f" (`{config_name}`)" if config_name else ""
lines = [
"# Справка конфигурации",
"",
"Автогенерация из встроенной справки 1С (`Ext/Help/ru.html`).",
"",
- f"- **Версия конфигурации (эта выгрузка):** `{config_version}`{name_line}",
+ f"- **Конфигурация:** {identity.display_name or '—'}",
+ f"- **Версия (эта выгрузка):** `{identity.version}`",
+ f"- **Разработчик:** {identity.vendor or '—'}",
f"- Источник: `{source_label}`",
f"- Страниц: **{len(pages)}** "
f"(+{life.created_count} / ~{life.edited_count} / ={life.unchanged_count} / -{life.deleted_count})",
@@ -133,16 +136,15 @@ def build_sidebar(pages: list[HelpPage], titles: dict[str, str]) -> str:
def build_history(
*,
- config_version: str,
- config_name: str | None,
+ identity: ConfigIdentity,
life: LifecycleResult,
prev_exports: list[dict],
) -> str:
- name = f" `{config_name}`" if config_name else ""
lines = [
"# История выгрузок справки",
"",
- f"Текущая выгрузка: конфигурация **{config_version}**{name}.",
+ f"Текущая выгрузка: **{identity.display_name or '—'}** "
+ f"версия **{identity.version}**, разработчик **{identity.vendor or '—'}**.",
"",
"## Эта выгрузка",
"",
@@ -157,7 +159,7 @@ def build_history(
lines.append("### Удалено в этой версии")
lines.append("")
for rec in life.deleted:
- if rec.deleted_in != config_version:
+ if rec.deleted_in != identity.version:
continue
lines.append(
f"- `{rec.meta_key}` — «{rec.title}» "
@@ -183,11 +185,14 @@ def build_history(
lines.append("_Нет сохранённой истории (первая выгрузка с учётом версий)._")
lines.append("")
else:
- lines.append("| Версия конфигурации | Дата (UTC) | Страниц | + | ~ | - |")
- lines.append("|---------------------|------------|---------|---|---|---|")
+ lines.append("| Конфигурация | Версия | Разработчик | Дата (UTC) | Страниц | + | ~ | - |")
+ lines.append("|--------------|--------|-------------|------------|---------|---|---|---|")
for row in prev_exports:
lines.append(
- f"| {row.get('config_version', '?')} | {row.get('exported_at', '?')} | "
+ f"| {row.get('config_display') or row.get('config_name') or '—'} | "
+ f"{row.get('config_version', '?')} | "
+ f"{row.get('config_vendor') or '—'} | "
+ f"{row.get('exported_at', '?')} | "
f"{row.get('pages_written', '?')} | {row.get('created', '?')} | "
f"{row.get('edited', '?')} | {row.get('deleted', '?')} |"
)
@@ -212,8 +217,15 @@ def export_help(
out_dir = out_dir.resolve()
stats = ExportStats()
- cfg_ver = (config_version or read_config_version(src_root)).strip() or "unknown"
- cfg_name = read_config_name(src_root)
+ identity = read_config_identity(src_root)
+ if config_version:
+ identity = ConfigIdentity(
+ version=config_version.strip() or identity.version,
+ name=identity.name,
+ synonym=identity.synonym,
+ vendor=identity.vendor,
+ )
+ cfg_ver = identity.version
stats.config_version = cfg_ver
prev_manifest = load_prev_manifest(prev_manifest_path)
@@ -286,7 +298,7 @@ def export_help(
for rec in life.active:
body = body_by_meta[rec.meta_key]
- md = body.rstrip() + _page_footer(rec, config_version=cfg_ver)
+ md = body.rstrip() + _page_footer(rec, identity=identity)
(out_dir / rec.filename).write_text(md, encoding="utf-8")
stats.pages_written += 1
@@ -312,6 +324,9 @@ def export_help(
0,
{
"config_version": prev_manifest.get("config_version"),
+ "config_name": prev_manifest.get("config_name"),
+ "config_display": prev_manifest.get("config_display"),
+ "config_vendor": prev_manifest.get("config_vendor"),
"exported_at": prev_manifest.get("exported_at"),
"pages_written": prev_manifest.get("pages_written"),
"created": prev_manifest.get("created_count"),
@@ -332,8 +347,7 @@ def export_help(
written_pages,
titles,
source_label=label,
- config_version=cfg_ver,
- config_name=cfg_name,
+ identity=identity,
life=life,
),
encoding="utf-8",
@@ -344,8 +358,7 @@ def export_help(
)
(out_dir / "History.md").write_text(
build_history(
- config_version=cfg_ver,
- config_name=cfg_name,
+ identity=identity,
life=life,
prev_exports=prev_exports,
),
@@ -356,6 +369,9 @@ def export_help(
exports = [
{
"config_version": cfg_ver,
+ "config_name": identity.name,
+ "config_display": identity.display_name,
+ "config_vendor": identity.vendor,
"exported_at": exported_at,
"pages_written": stats.pages_written,
"created": stats.created_count,
@@ -370,7 +386,9 @@ def export_help(
"tool": "export_1c_help",
"version": __version__,
"config_version": cfg_ver,
- "config_name": cfg_name,
+ "config_name": identity.name,
+ "config_display": identity.display_name,
+ "config_vendor": identity.vendor,
"source": label,
"exported_at": exported_at,
"pages_total": stats.pages_total,
diff --git a/export_1c_help.py b/export_1c_help.py
index dedfabc..59fde4b 100644
--- a/export_1c_help.py
+++ b/export_1c_help.py
@@ -13,7 +13,7 @@ if str(TOOL_ROOT) not in sys.path:
from export1c_help import __status__, __version__ # noqa: E402
from export1c_help.config_version import ( # noqa: E402
- read_config_name,
+ read_config_identity,
read_config_version,
)
from export1c_help.export import export_help # noqa: E402
@@ -190,6 +190,7 @@ def cmd_info(args: argparse.Namespace) -> int:
src = _resolve_src(args)
root = resolve_config_root(src)
wiki = resolve_wiki_url(src, wiki_url=args.wiki_url, remote=args.remote)
+ identity = read_config_identity(src)
except ResolveError as exc:
print(f"error: {exc}", file=sys.stderr)
return 1
@@ -198,8 +199,10 @@ def cmd_info(args: argparse.Namespace) -> int:
remote_url = git_remote_url(root, remote=args.remote)
print(f"config_root: {root}")
print(f"src: {src}")
- print(f"config_name: {read_config_name(src) or '—'}")
- print(f"config_version: {read_config_version(src)}")
+ print(f"config_name: {identity.name or '—'}")
+ print(f"config_display: {identity.display_name or '—'}")
+ print(f"config_vendor: {identity.vendor or '—'}")
+ print(f"config_version: {identity.version}")
print(f"git_toplevel: {top or '—'}")
print(f"git_remote({args.remote}): {remote_url or '—'}")
print(f"wiki_url: {wiki}")