From c47879098ec4b8f91f2738b8da59f7cf920a7ce4 Mon Sep 17 00:00:00 2001 From: Nick Shirokov Date: Tue, 29 Sep 2026 13:00:51 +0300 Subject: [PATCH] =?UTF-8?q?feat(v8-xsd-fetch):=20=D0=B7=D0=B0=D0=B3=D1=80?= =?UTF-8?q?=D1=83=D0=B7=D0=BA=D0=B0=20XSD-=D1=81=D1=85=D0=B5=D0=BC=20?= =?UTF-8?q?=D0=BF=D0=BB=D0=B0=D1=82=D1=84=D0=BE=D1=80=D0=BC=D1=8B=20=D0=B2?= =?UTF-8?q?=20.v8-xsd?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Схемы формата выгрузки по версиям формата из yellow-hammer/namespace-forest раскладываются в .v8-xsd/<версия>/ (или по xsdPath из .v8-project.json) — основа проверки выгрузки по XSD без базы (#101). Сами схемы — собственность 1С-Софт, в репозиторий не входят: в .gitignore, в каталоге только README. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_018dc2yLQfw6wppbopM1Y4Ps --- .claude/skills/db-list/SKILL.md | 1 + .claude/skills/v8-xsd-fetch/SKILL.md | 44 ++++ .../v8-xsd-fetch/scripts/v8-xsd-fetch.ps1 | 175 +++++++++++++++ .../v8-xsd-fetch/scripts/v8-xsd-fetch.py | 207 ++++++++++++++++++ .../skills/v8-xsd-fetch/templates/README.md | 39 ++++ .gitignore | 4 + .v8-xsd/README.md | 39 ++++ README.md | 5 +- docs/v8-project-guide.md | 12 + tests/skills/cases/v8-xsd-fetch/_skill.json | 5 + .../cases/v8-xsd-fetch/error-bad-version.json | 9 + .../v8-xsd-fetch/error-outpath-is-file.json | 10 + tests/skills/check-inline-drift.mjs | 4 +- 13 files changed, 550 insertions(+), 4 deletions(-) create mode 100644 .claude/skills/v8-xsd-fetch/SKILL.md create mode 100644 .claude/skills/v8-xsd-fetch/scripts/v8-xsd-fetch.ps1 create mode 100644 .claude/skills/v8-xsd-fetch/scripts/v8-xsd-fetch.py create mode 100644 .claude/skills/v8-xsd-fetch/templates/README.md create mode 100644 .v8-xsd/README.md create mode 100644 tests/skills/cases/v8-xsd-fetch/_skill.json create mode 100644 tests/skills/cases/v8-xsd-fetch/error-bad-version.json create mode 100644 tests/skills/cases/v8-xsd-fetch/error-outpath-is-file.json diff --git a/.claude/skills/db-list/SKILL.md b/.claude/skills/db-list/SKILL.md index b8921d102..915524c5d 100644 --- a/.claude/skills/db-list/SKILL.md +++ b/.claude/skills/db-list/SKILL.md @@ -80,6 +80,7 @@ allowed-tools: | `newObjectPosition` | `"end"` / `"byName"` | Куда попадает новый объект в составе конфигурации: в конец своего вида (по умолчанию) или на место по имени. Переопределяется в `databases[]` | | `extensionApplyCheck` | bool | Проверять ли применимость расширения после загрузки в базу (по умолчанию `true`). Разово отключается ключом `-NoApplyCheck` | | `externalCheck` | bool | Проверять ли исходники внешней обработки/отчёта платформой перед сборкой (по умолчанию `true`). Разово отключается ключом `-Checks off` | +| `xsdPath` | string | Каталог XSD-схем платформы для проверки выгрузок по версиям формата; по умолчанию `.v8-xsd` рядом с этим файлом. Наполняется `/v8-xsd-fetch` | | `databases` | array | Массив баз данных | | `default` | string | id базы по умолчанию | diff --git a/.claude/skills/v8-xsd-fetch/SKILL.md b/.claude/skills/v8-xsd-fetch/SKILL.md new file mode 100644 index 000000000..49eccdcf1 --- /dev/null +++ b/.claude/skills/v8-xsd-fetch/SKILL.md @@ -0,0 +1,44 @@ +--- +name: v8-xsd-fetch +description: Загрузка XSD-схем формата XML-выгрузки 1С по версиям формата в каталог проекта. Используй когда нужно скачать или обновить схемы платформы для проверки выгрузки по XSD, или узнать, какие версии схем доступны +argument-hint: "[-Versions 2.20,2.21] [-List]" +allowed-tools: + - Bash + - Read +--- + +# /v8-xsd-fetch — XSD-схемы платформы + +Скачивает XSD-схемы формата выгрузки из репозитория +[yellow-hammer/namespace-forest](https://github.com/yellow-hammer/namespace-forest) +и раскладывает по версиям формата: `.v8-xsd/<версия>/*.xsd`. По этим схемам навыки +проверяют выгрузку без базы; нет схем — нет проверки. + +## Команда + +```powershell +powershell.exe -NoProfile -File "${CLAUDE_SKILL_DIR}/scripts/v8-xsd-fetch.ps1" [-Versions "2.20,2.21"] [-List] +``` + +| Параметр | Описание | +|----------|----------| +| `-Versions "<список>"` | Версии формата через запятую. Без него — все доступные (около 600 КБ на версию) | +| `-List` | Показать доступные версии и какие уже есть локально, ничего не скачивая | +| `-OutPath <путь>` | Каталог схем, если нужен не тот, что в проекте | +| `-Force` | Перекачать файлы, которые уже есть | +| `-Ref <ветка/коммит>` | Состояние репозитория-источника, по умолчанию `main` | + +## Куда кладёт + +`-OutPath` → `xsdPath` из `.v8-project.json` (относительно файла) → `.v8-xsd` рядом с +`.v8-project.json` → `.v8-xsd` в текущем каталоге. + +Рядом со схемами — `README.md` (что это и откуда) и `source.json` (репозиторий, коммит, дата). +Схемы — собственность ООО «1С-Софт»: каталог в Git не добавлять, его место в `.gitignore`. + +## Какую версию брать + +Версия формата — атрибут `version` корня `Configuration.xml` выгрузки (например, `2.20`), +а не номер релиза платформы. Достаточно версий тех выгрузок, с которыми работает проект. + +Без сети: скопировать каталоги `schemas/designer/<версия>/` из репозитория-источника вручную. diff --git a/.claude/skills/v8-xsd-fetch/scripts/v8-xsd-fetch.ps1 b/.claude/skills/v8-xsd-fetch/scripts/v8-xsd-fetch.ps1 new file mode 100644 index 000000000..579eb3110 --- /dev/null +++ b/.claude/skills/v8-xsd-fetch/scripts/v8-xsd-fetch.ps1 @@ -0,0 +1,175 @@ +# v8-xsd-fetch v1.0 — Download 1C platform XSD schemas (dump format) into .v8-xsd +# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills +<# +.SYNOPSIS + Загрузка XSD-схем формата выгрузки 1С по версиям формата + +.DESCRIPTION + Скачивает schemas/designer/<версия>/*.xsd из репозитория yellow-hammer/namespace-forest + в каталог схем проекта: -OutPath, иначе xsdPath из .v8-project.json, иначе .v8-xsd + рядом с .v8-project.json (нет его — в текущем каталоге). + +.EXAMPLE + .\v8-xsd-fetch.ps1 -Versions "2.20,2.21" + +.EXAMPLE + .\v8-xsd-fetch.ps1 -List +#> + +[CmdletBinding(PositionalBinding=$false)] +param( + # Списком одной строкой: powershell -File не связывает массивы + [Parameter(Mandatory=$false)] + [string]$Versions, + + [Parameter(Mandatory=$false)] + [string]$OutPath, + + [Parameter(Mandatory=$false)] + [string]$Ref = "main", + + [Parameter(Mandatory=$false)] + [switch]$Force, + + [Parameter(Mandatory=$false)] + [switch]$List +) + +$ErrorActionPreference = "Stop" +[Console]::OutputEncoding = [System.Text.Encoding]::UTF8 + +$repo = "yellow-hammer/namespace-forest" +$repoUrl = "https://github.com/$repo" + +# Текст README — в templates/ навыка, одна копия на оба порта +$readmeTemplate = Join-Path $PSScriptRoot "../templates/README.md" + +function Find-V8Project([string]$startDir) { + $d = $startDir + for ($i = 0; $i -lt 20 -and $d; $i++) { + $pj = Join-Path $d ".v8-project.json" + if (Test-Path $pj) { return $pj } + $parent = [System.IO.Path]::GetDirectoryName($d) + if ($parent -eq $d) { break } + $d = $parent + } + return $null +} + +function Fail([string]$msg) { + Write-Host "Error: $msg" -ForegroundColor Red + exit 1 +} + +# --- Версии: проверяем до сети --- +$wanted = @() +if ($Versions) { + foreach ($v in ($Versions -split '[,;\s]+')) { + if (-not $v) { continue } + if ($v -notmatch '^\d+\.\d+$') { Fail "неверная версия формата '$v' (ожидается вида 2.20)" } + if ($wanted -notcontains $v) { $wanted += $v } + } +} + +# --- Каталог схем --- +$cwd = (Get-Location).Path +$pj = Find-V8Project $cwd +$target = $null +if ($OutPath) { + $target = if ([System.IO.Path]::IsPathRooted($OutPath)) { $OutPath } else { Join-Path $cwd $OutPath } +} elseif ($pj) { + $pjDir = [System.IO.Path]::GetDirectoryName($pj) + try { $cfg = Get-Content -LiteralPath $pj -Raw -Encoding UTF8 | ConvertFrom-Json } catch { $cfg = $null } + if ($cfg -and $cfg.xsdPath) { + $target = if ([System.IO.Path]::IsPathRooted($cfg.xsdPath)) { $cfg.xsdPath } else { Join-Path $pjDir $cfg.xsdPath } + } else { + $target = Join-Path $pjDir ".v8-xsd" + } +} else { + $target = Join-Path $cwd ".v8-xsd" +} +$target = [System.IO.Path]::GetFullPath($target) +if (Test-Path -LiteralPath $target -PathType Leaf) { Fail "каталог схем '$target' — это файл" } + +# --- Состав репозитория --- +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$wc = New-Object System.Net.WebClient +$wc.Headers.Add("User-Agent", "v8-xsd-fetch") +$wc.Encoding = [System.Text.Encoding]::UTF8 +try { + $tree = $wc.DownloadString("https://api.github.com/repos/$repo/git/trees/$([uri]::EscapeDataString($Ref))?recursive=1") | ConvertFrom-Json +} catch { + Fail "не удалось получить состав $repo@${Ref}: $($_.Exception.Message)`nСкачайте вручную каталоги schemas/designer/<версия> из $repoUrl в '$target'" +} +$commit = $tree.sha + +$byVersion = @{} +foreach ($node in $tree.tree) { + if ($node.type -ne 'blob') { continue } + $m = [regex]::Match($node.path, '^schemas/designer/(\d+\.\d+)/([^/]+\.xsd)$') + if (-not $m.Success) { continue } + $v = $m.Groups[1].Value + if (-not $byVersion.ContainsKey($v)) { $byVersion[$v] = New-Object System.Collections.ArrayList } + [void]$byVersion[$v].Add(@{ path = $node.path; name = $m.Groups[2].Value }) +} +$available = @($byVersion.Keys | Sort-Object { [version]$_ }) +if ($available.Count -eq 0) { Fail "в $repo@$Ref нет каталогов schemas/designer/<версия>" } + +if ($List) { + Write-Host "Версии формата в $repo@${Ref}:" + foreach ($v in $available) { + $mark = if (Test-Path -LiteralPath (Join-Path $target $v) -PathType Container) { " (есть локально)" } else { "" } + Write-Host " $v — $($byVersion[$v].Count) схем$mark" + } + Write-Host "Каталог схем: $target" + exit 0 +} + +if ($wanted.Count -eq 0) { $wanted = $available } +$missing = @($wanted | Where-Object { $available -notcontains $_ }) +if ($missing.Count -gt 0) { + Fail "версии $($missing -join ', ') нет в $repo@$Ref (есть: $($available -join ', '))" +} + +# --- Загрузка --- +if (-not (Test-Path -LiteralPath $target)) { New-Item -ItemType Directory -Path $target -Force | Out-Null } +$totalNew = 0 +foreach ($v in ($wanted | Sort-Object { [version]$_ })) { + $dir = Join-Path $target $v + if (-not (Test-Path -LiteralPath $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } + $got = 0; $kept = 0 + foreach ($f in $byVersion[$v]) { + $dest = Join-Path $dir $f.name + if ((Test-Path -LiteralPath $dest) -and -not $Force) { $kept++; continue } + # Файлы берём по коммиту, а не по ветке: весь набор — из одного состояния репозитория + $url = "https://raw.githubusercontent.com/$repo/$commit/$($f.path)" + try { + $bytes = $wc.DownloadData($url) + } catch { + Fail "не удалось скачать $($f.path): $($_.Exception.Message)" + } + [System.IO.File]::WriteAllBytes($dest, $bytes) + $got++ + } + $totalNew += $got + $line = " ${v}: скачано $got" + if ($kept -gt 0) { $line += ", уже было $kept" } + Write-Host $line +} + +# --- README и source.json --- +$readmePath = Join-Path $target "README.md" +if (-not (Test-Path -LiteralPath $readmePath) -and (Test-Path -LiteralPath $readmeTemplate)) { + Copy-Item -LiteralPath $readmeTemplate -Destination $readmePath +} +$localVersions = @(Get-ChildItem -LiteralPath $target -Directory | + Where-Object { $_.Name -match '^\d+\.\d+$' } | + ForEach-Object { $_.Name } | Sort-Object { [version]$_ }) +# Руками, а не ConvertTo-Json: форматирование PS 5.1 не совпало бы с py-портом +$fetched = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ") +$verJson = ($localVersions | ForEach-Object { "`"$_`"" }) -join ", " +$srcJson = "{`n `"repository`": `"$repoUrl`",`n `"ref`": `"$($Ref -replace '\\','\\' -replace '"','\"')`",`n `"commit`": `"$commit`",`n `"fetched`": `"$fetched`",`n `"versions`": [$verJson]`n}`n" +$enc = New-Object System.Text.UTF8Encoding($false) +[System.IO.File]::WriteAllText((Join-Path $target "source.json"), $srcJson, $enc) + +Write-Host "Схемы: $target (скачано файлов: $totalNew)" diff --git a/.claude/skills/v8-xsd-fetch/scripts/v8-xsd-fetch.py b/.claude/skills/v8-xsd-fetch/scripts/v8-xsd-fetch.py new file mode 100644 index 000000000..861a54e40 --- /dev/null +++ b/.claude/skills/v8-xsd-fetch/scripts/v8-xsd-fetch.py @@ -0,0 +1,207 @@ +#!/usr/bin/env python3 +# v8-xsd-fetch v1.0 — Download 1C platform XSD schemas (dump format) into .v8-xsd +# Source: https://github.com/Nikolay-Shirokov/cc-1c-skills +"""Загрузка XSD-схем формата выгрузки 1С по версиям формата. + +Скачивает schemas/designer/<версия>/*.xsd из репозитория yellow-hammer/namespace-forest +в каталог схем проекта: -OutPath, иначе xsdPath из .v8-project.json, иначе .v8-xsd +рядом с .v8-project.json (нет его — в текущем каталоге). +""" +import argparse +import json +import os +import re +import shutil +import sys +import urllib.parse +import urllib.request +from datetime import datetime, timezone + +sys.stdout.reconfigure(encoding="utf-8") +sys.stderr.reconfigure(encoding="utf-8") + +REPO = "yellow-hammer/namespace-forest" +REPO_URL = "https://github.com/" + REPO +TIMEOUT = 60 + +# Текст README — в templates/ навыка, одна копия на оба порта +README_TEMPLATE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "templates", "README.md") + + +def ci_parse_args(parser, argv=None): + """parse_args по правилам PS: имена параметров и значения choices регистронезависимы.""" + argv = list(sys.argv[1:] if argv is None else argv) + names = {s.lower(): s for a in parser._actions for s in a.option_strings} + for i, tok in enumerate(argv): + if tok.startswith('-') and tok.lower() in names: + argv[i] = names[tok.lower()] + # choices — зеркало [ValidateSet]; канонизируем ДО разбора, иначе argparse отвергнет регистр + choice_map = {} + for a in parser._actions: + if a.choices: + for s in a.option_strings: + choice_map[s] = {str(c).lower(): c for c in a.choices} + for i in range(len(argv) - 1): + m = choice_map.get(argv[i]) + if m and argv[i + 1].lower() in m: + argv[i + 1] = m[argv[i + 1].lower()] + return parser.parse_args(argv) + + +def _sg_find_v8project(start_dir): + d = start_dir + for _ in range(20): + if not d: + break + pj = os.path.join(d, ".v8-project.json") + if os.path.isfile(pj): + return pj + parent = os.path.dirname(d) + if parent == d: + break + d = parent + return None + + +def fail(msg): + print("Error: " + msg) + sys.exit(1) + + +def version_key(v): + return tuple(int(x) for x in v.split(".")) + + +def http_get(url): + req = urllib.request.Request(url, headers={"User-Agent": "v8-xsd-fetch"}) + with urllib.request.urlopen(req, timeout=TIMEOUT) as resp: + return resp.read() + + +def main(): + parser = argparse.ArgumentParser(description="Download 1C platform XSD schemas into .v8-xsd", allow_abbrev=False) + # Списком одной строкой — паритет с PS: powershell -File не связывает массивы + parser.add_argument("-Versions", default="") + parser.add_argument("-OutPath", default="") + parser.add_argument("-Ref", default="main") + parser.add_argument("-Force", action="store_true") + parser.add_argument("-List", action="store_true") + args = ci_parse_args(parser) + ref = args.Ref + + # --- Версии: проверяем до сети --- + wanted = [] + if args.Versions: + for v in re.split(r"[,;\s]+", args.Versions): + if not v: + continue + if not re.match(r"^\d+\.\d+$", v): + fail("неверная версия формата '%s' (ожидается вида 2.20)" % v) + if v not in wanted: + wanted.append(v) + + # --- Каталог схем --- + cwd = os.getcwd() + pj = _sg_find_v8project(cwd) + if args.OutPath: + target = args.OutPath if os.path.isabs(args.OutPath) else os.path.join(cwd, args.OutPath) + elif pj: + pj_dir = os.path.dirname(pj) + try: + with open(pj, encoding="utf-8-sig") as f: + cfg = json.load(f) + except Exception: + cfg = None + xsd_path = cfg.get("xsdPath") if isinstance(cfg, dict) else None + if xsd_path: + target = xsd_path if os.path.isabs(xsd_path) else os.path.join(pj_dir, xsd_path) + else: + target = os.path.join(pj_dir, ".v8-xsd") + else: + target = os.path.join(cwd, ".v8-xsd") + target = os.path.abspath(target) + if os.path.isfile(target): + fail("каталог схем '%s' — это файл" % target) + + # --- Состав репозитория --- + try: + tree = json.loads(http_get("https://api.github.com/repos/%s/git/trees/%s?recursive=1" + % (REPO, urllib.parse.quote(ref, safe=""))).decode("utf-8")) + except Exception as e: + fail("не удалось получить состав %s@%s: %s\nСкачайте вручную каталоги schemas/designer/<версия> из %s в '%s'" + % (REPO, ref, e, REPO_URL, target)) + commit = tree.get("sha", "") + + by_version = {} + for node in tree.get("tree", []): + if node.get("type") != "blob": + continue + m = re.match(r"^schemas/designer/(\d+\.\d+)/([^/]+\.xsd)$", node.get("path", "")) + if not m: + continue + by_version.setdefault(m.group(1), []).append({"path": node["path"], "name": m.group(2)}) + available = sorted(by_version.keys(), key=version_key) + if not available: + fail("в %s@%s нет каталогов schemas/designer/<версия>" % (REPO, ref)) + + if args.List: + print("Версии формата в %s@%s:" % (REPO, ref)) + for v in available: + mark = " (есть локально)" if os.path.isdir(os.path.join(target, v)) else "" + print(" %s — %d схем%s" % (v, len(by_version[v]), mark)) + print("Каталог схем: " + target) + sys.exit(0) + + if not wanted: + wanted = list(available) + missing = [v for v in wanted if v not in available] + if missing: + fail("версии %s нет в %s@%s (есть: %s)" % (", ".join(missing), REPO, ref, ", ".join(available))) + + # --- Загрузка --- + os.makedirs(target, exist_ok=True) + total_new = 0 + for v in sorted(wanted, key=version_key): + vdir = os.path.join(target, v) + os.makedirs(vdir, exist_ok=True) + got = kept = 0 + for f in by_version[v]: + dest = os.path.join(vdir, f["name"]) + if os.path.exists(dest) and not args.Force: + kept += 1 + continue + # Файлы берём по коммиту, а не по ветке: весь набор — из одного состояния репозитория + url = "https://raw.githubusercontent.com/%s/%s/%s" % (REPO, commit, f["path"]) + try: + data = http_get(url) + except Exception as e: + fail("не удалось скачать %s: %s" % (f["path"], e)) + with open(dest, "wb") as out: + out.write(data) + got += 1 + total_new += got + line = " %s: скачано %d" % (v, got) + if kept > 0: + line += ", уже было %d" % kept + print(line) + + # --- README и source.json --- + readme_path = os.path.join(target, "README.md") + if not os.path.exists(readme_path) and os.path.isfile(README_TEMPLATE): + shutil.copyfile(README_TEMPLATE, readme_path) + local_versions = sorted((d for d in os.listdir(target) + if re.match(r"^\d+\.\d+$", d) and os.path.isdir(os.path.join(target, d))), + key=version_key) + fetched = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + ver_json = ", ".join('"%s"' % v for v in local_versions) + ref_json = ref.replace("\\", "\\\\").replace('"', '\\"') + src_json = ('{\n "repository": "%s",\n "ref": "%s",\n "commit": "%s",\n "fetched": "%s",\n "versions": [%s]\n}\n' + % (REPO_URL, ref_json, commit, fetched, ver_json)) + with open(os.path.join(target, "source.json"), "w", encoding="utf-8", newline="\n") as out: + out.write(src_json) + + print("Схемы: %s (скачано файлов: %d)" % (target, total_new)) + + +if __name__ == "__main__": + main() diff --git a/.claude/skills/v8-xsd-fetch/templates/README.md b/.claude/skills/v8-xsd-fetch/templates/README.md new file mode 100644 index 000000000..9a5ed5b7a --- /dev/null +++ b/.claude/skills/v8-xsd-fetch/templates/README.md @@ -0,0 +1,39 @@ +# .v8-xsd — XSD-схемы платформы 1С:Предприятие 8 + +Каталог для XSD-схем формата XML-выгрузки конфигурации. По ним навыки проверяют выгрузку +без базы: порядок элементов, типы значений, допустимые значения перечислений. + +Схем нет — проверки нет, остальное работает как обычно. + +## Раскладка + +``` +.v8-xsd/ +├── 2.20/ схемы версии формата 2.20 (атрибут version корня MetaDataObject) +│ └── *.xsd +├── 2.21/ +└── source.json откуда и когда загружено +``` + +Имена файлов значения не имеют: схема находится по `targetNamespace`. Подойдут и схемы, +выгруженные из своей платформы (`ФабрикаXDTO.ЭкспортСхемыXML`). + +## Как наполнить + +Навыком `/v8-xsd-fetch` — он скачивает схемы из репозитория +[yellow-hammer/namespace-forest](https://github.com/yellow-hammer/namespace-forest) +(каталог `schemas/designer/<версия формата>/`). + +Или вручную: скопировать нужные каталоги версий из `schemas/designer/` того же репозитория. + +Другой каталог задаётся ключом `xsdPath` в `.v8-project.json`. + +## Лицензия + +Схемы (`*.xsd`) — часть продукта «1С:Предприятие 8», правообладатель ООО «1С-Софт». +Использование регулируется лицензионным соглашением 1С. В репозиторий их не включаем. + +## Благодарность + +Спасибо Ivan Karlo за [namespace-forest](https://github.com/yellow-hammer/namespace-forest) — +собранные и разложенные по версиям формата схемы, обновляемые автоматически. diff --git a/.gitignore b/.gitignore index 8c4d090d6..e6785a8ed 100644 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,10 @@ __pycache__/ # на чистом клоне не сойдётся с результатом прогона. !tests/skills/cases/**/.v8-project.json +# XSD-схемы платформы (/v8-xsd-fetch) — собственность 1С, не распространяем +.v8-xsd/* +!.v8-xsd/README.md + # web-test: Node.js зависимости и runtime-артефакты .claude/skills/web-test/scripts/node_modules/ .claude/skills/web-test/.browser-session.json diff --git a/.v8-xsd/README.md b/.v8-xsd/README.md new file mode 100644 index 000000000..9a5ed5b7a --- /dev/null +++ b/.v8-xsd/README.md @@ -0,0 +1,39 @@ +# .v8-xsd — XSD-схемы платформы 1С:Предприятие 8 + +Каталог для XSD-схем формата XML-выгрузки конфигурации. По ним навыки проверяют выгрузку +без базы: порядок элементов, типы значений, допустимые значения перечислений. + +Схем нет — проверки нет, остальное работает как обычно. + +## Раскладка + +``` +.v8-xsd/ +├── 2.20/ схемы версии формата 2.20 (атрибут version корня MetaDataObject) +│ └── *.xsd +├── 2.21/ +└── source.json откуда и когда загружено +``` + +Имена файлов значения не имеют: схема находится по `targetNamespace`. Подойдут и схемы, +выгруженные из своей платформы (`ФабрикаXDTO.ЭкспортСхемыXML`). + +## Как наполнить + +Навыком `/v8-xsd-fetch` — он скачивает схемы из репозитория +[yellow-hammer/namespace-forest](https://github.com/yellow-hammer/namespace-forest) +(каталог `schemas/designer/<версия формата>/`). + +Или вручную: скопировать нужные каталоги версий из `schemas/designer/` того же репозитория. + +Другой каталог задаётся ключом `xsdPath` в `.v8-project.json`. + +## Лицензия + +Схемы (`*.xsd`) — часть продукта «1С:Предприятие 8», правообладатель ООО «1С-Софт». +Использование регулируется лицензионным соглашением 1С. В репозиторий их не включаем. + +## Благодарность + +Спасибо Ivan Karlo за [namespace-forest](https://github.com/yellow-hammer/namespace-forest) — +собранные и разложенные по версиям формата схемы, обновляемые автоматически. diff --git a/README.md b/README.md index 9e0a2e9c2..9e44ff55d 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ python tools/cc-1c-skills/scripts/switch.py | Тестирование (Web) | `/web-test` | Взаимодействие с веб-клиентом 1С — навигация, формы, таблицы, отчёты, тестирование | [Подробнее](docs/web-test-guide.md) | | Запись видео (Web) | `/web-test` | Запись видеоинструкций с субтитрами, подсветкой и TTS-озвучкой | [Подробнее](docs/web-test-recording-guide.md) | | Регресс прикладного решения (Web) | `/web-test` | Автоматический регресс конфигурации: тесты, проверки, отчёты, прогон после правок | [Подробнее](docs/web-test-regression-guide.md) | -| Утилиты | `/img-grid` | Наложение сетки на изображение для определения пропорций колонок | — | +| Утилиты | `/img-grid`, `/v8-xsd-fetch` | Сетка на изображении для определения пропорций колонок; загрузка XSD-схем платформы для проверки выгрузок | [Подробнее](docs/v8-project-guide.md#xsdpath--xsd-схемы-платформы) | ## Требования @@ -246,7 +246,8 @@ python scripts/switch.py --runtime powershell # вернуть на PowerShell ├── web-stop/ # Остановка Apache ├── web-unpublish/ # Удаление публикации ├── web-test/ # Тестирование через веб-клиент 1С -└── img-grid/ # Сетка для анализа изображений +├── img-grid/ # Сетка для анализа изображений +└── v8-xsd-fetch/ # Загрузка XSD-схем платформы (.v8-xsd) scripts/ └── switch.py # Переключение платформы и рантайма (13 платформ) docs/ diff --git a/docs/v8-project-guide.md b/docs/v8-project-guide.md index 5e0821d66..d7d3dcb04 100644 --- a/docs/v8-project-guide.md +++ b/docs/v8-project-guide.md @@ -88,6 +88,7 @@ | `webPath` | string | нет | `tools/apache24` | Каталог Apache HTTP Server | Руками | | `ffmpegPath` | string | нет | `tools/ffmpeg/bin/ffmpeg.exe` | Путь к ffmpeg | Руками | | `tts` | object | нет | Edge TTS, DmitryNeural | Настройки озвучки видео | Руками | +| `xsdPath` | string | нет | `.v8-xsd` | Каталог XSD-схем платформы по версиям формата (см. ниже) | Руками | ## Базы данных (`databases[]`) @@ -257,6 +258,16 @@ Подробнее о выборе провайдера и голосов — в [гайде по записи видео](web-test-recording-guide.md#доступные-голоса-и-провайдеры). +### `xsdPath` — XSD-схемы платформы + +Каталог XSD-схем формата выгрузки, разложенных по версиям формата: `/<версия>/*.xsd`. +По ним проверяется выгрузка без базы — порядок элементов, типы, допустимые значения. + +Если не задан — `.v8-xsd` рядом с `.v8-project.json`. Путь считается от каталога `.v8-project.json`. +Наполняется навыком `/v8-xsd-fetch` из репозитория +[yellow-hammer/namespace-forest](https://github.com/yellow-hammer/namespace-forest). Схемы — +собственность ООО «1С-Софт», в Git их не добавляют. Нет схем — нет проверки. + ### `webUrl` — URL веб-клиента (per-database) URL для открытия базы в браузере через `/web-test`. Задаётся в записи конкретной базы. @@ -275,6 +286,7 @@ src/ cfe/<ИмяРасширения>/ # исходники расширения (CFE) epf/<ИмяОбработки>/ # исходники внешней обработки (EPF) erf/<ИмяОтчёта>/ # исходники внешнего отчёта (ERF) +.v8-xsd/ # XSD-схемы платформы по версиям формата (/v8-xsd-fetch; в .gitignore) ``` В `.v8-project.json` напрямую участвует только `cf` — через поле `configSrc` базы (`"src\\cf"`). diff --git a/tests/skills/cases/v8-xsd-fetch/_skill.json b/tests/skills/cases/v8-xsd-fetch/_skill.json new file mode 100644 index 000000000..f3e41774f --- /dev/null +++ b/tests/skills/cases/v8-xsd-fetch/_skill.json @@ -0,0 +1,5 @@ +{ + "script": "v8-xsd-fetch/scripts/v8-xsd-fetch", + "setup": "none", + "args": [] +} diff --git a/tests/skills/cases/v8-xsd-fetch/error-bad-version.json b/tests/skills/cases/v8-xsd-fetch/error-bad-version.json new file mode 100644 index 000000000..49eba4528 --- /dev/null +++ b/tests/skills/cases/v8-xsd-fetch/error-bad-version.json @@ -0,0 +1,9 @@ +{ + "name": "версия не вида N.N — отказ до обращения к сети", + "args_extra": ["-Versions", "2.20,2.x"], + "expectError": true, + "expect": { + "stdoutContains": "неверная версия формата '2.x'" + }, + "noSnapshot": "негативный кейс — навык отказывает до загрузки" +} diff --git a/tests/skills/cases/v8-xsd-fetch/error-outpath-is-file.json b/tests/skills/cases/v8-xsd-fetch/error-outpath-is-file.json new file mode 100644 index 000000000..7451f6ded --- /dev/null +++ b/tests/skills/cases/v8-xsd-fetch/error-outpath-is-file.json @@ -0,0 +1,10 @@ +{ + "name": "каталог схем указывает на файл — отказ до обращения к сети", + "preRun": [{ "writeFile": { "path": "schemas", "content": "x" } }], + "args_extra": ["-OutPath", "{workDir}/schemas", "-Versions", "2.20"], + "expectError": true, + "expect": { + "stdoutContains": "это файл" + }, + "noSnapshot": "негативный кейс — навык отказывает до загрузки" +} diff --git a/tests/skills/check-inline-drift.mjs b/tests/skills/check-inline-drift.mjs index bb6f121b6..3d45d2199 100644 --- a/tests/skills/check-inline-drift.mjs +++ b/tests/skills/check-inline-drift.mjs @@ -93,7 +93,7 @@ const FAMILIES = [ { id: 'full', authority: 'cf-edit', consumers: ['cfe-borrow', 'db-cfe-admin', 'db-create', 'db-dump-cf', 'db-dump-dt', 'db-dump-xml', 'db-load-cf', 'db-load-dt', 'db-load-git', 'db-load-xml', 'db-repo', 'db-run', 'db-update', - 'epf-build', 'epf-dump', 'web-publish', + 'epf-build', 'epf-dump', 'web-publish', 'v8-xsd-fetch', 'form-add', 'form-compile', 'form-edit', 'help-add', 'interface-edit', 'meta-compile', 'meta-edit', 'meta-remove', 'mxl-compile', 'role-compile', 'role-edit', 'skd-compile', 'skd-edit', 'subsystem-compile', 'subsystem-edit', 'template-add', 'xdto-compile', 'xdto-edit'] }, @@ -543,7 +543,7 @@ const FAMILIES = [ 'skd-info', 'skd-validate', 'subsystem-compile', 'subsystem-edit', 'subsystem-info', 'subsystem-validate', 'support-edit', 'template-add', 'template-remove', 'web-info', 'web-publish', 'web-stop', 'web-unpublish', 'xdto-compile', 'xdto-decompile', 'xdto-edit', - 'xdto-info', 'xdto-validate'] }, + 'xdto-info', 'xdto-validate', 'v8-xsd-fetch'] }, ], },