diff --git a/.claude/skills/db-cfe-admin/SKILL.md b/.claude/skills/db-cfe-admin/SKILL.md index 721942e68..a1c72d437 100644 --- a/.claude/skills/db-cfe-admin/SKILL.md +++ b/.claude/skills/db-cfe-admin/SKILL.md @@ -25,13 +25,13 @@ allowed-tools: ## Параметры подключения -Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` (путь к платформе) и разреши базу: +Прочитай `.v8-project.json` из корня проекта и разреши базу: 1. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую 2. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` 3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` 4. Если ветка не совпала — используй `default` -Если `v8path` не задан — скрипт сам попытается определить платформу. +Платформа — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. ## Команда diff --git a/.claude/skills/db-cfe-admin/scripts/db-cfe-admin.ps1 b/.claude/skills/db-cfe-admin/scripts/db-cfe-admin.ps1 index 63996d563..ee97a96ea 100644 --- a/.claude/skills/db-cfe-admin/scripts/db-cfe-admin.ps1 +++ b/.claude/skills/db-cfe-admin/scripts/db-cfe-admin.ps1 @@ -1,4 +1,4 @@ -# db-cfe-admin v1.0 — Configuration extensions in a 1C infobase: list, check, properties, delete +# db-cfe-admin v1.1 — Configuration extensions in a 1C infobase: list, check, properties, delete # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -110,9 +110,10 @@ $OutputEncoding = [System.Text.Encoding]::UTF8 # Копии держит одинаковыми tests/skills/check-inline-drift.mjs — правку вносить в навык-эталон. $Extension = $Name -# --- Реквизиты хранилища из .v8-project.json --- -# Модель их не передаёт: скрипт сопоставляет параметры соединения с записью в databases[] -# и берёт repository оттуда. Тот же приём, что в cf-edit.ps1 (сопоставление по configSrc). +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.ps1 (сопоставление по configSrc). function Find-V8Project([string]$startDir) { $d = $startDir for ($i = 0; $i -lt 20 -and $d; $i++) { @@ -396,6 +397,10 @@ Assert-InfoBaseExists $InfoBasePath # --- Resolve V8Path --- function Find-ProjectV8Path { + # v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + # платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + $dbRec = Find-ProjectDatabase + if ($dbRec -and $dbRec.v8path) { return [string]$dbRec.v8path } $dir = (Get-Location).Path while ($dir) { $pf = Join-Path $dir ".v8-project.json" diff --git a/.claude/skills/db-cfe-admin/scripts/db-cfe-admin.py b/.claude/skills/db-cfe-admin/scripts/db-cfe-admin.py index c62d459a6..b95008e9a 100644 --- a/.claude/skills/db-cfe-admin/scripts/db-cfe-admin.py +++ b/.claude/skills/db-cfe-admin/scripts/db-cfe-admin.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-cfe-admin v1.0 — Configuration extensions in a 1C infobase: list, check, properties, delete +# db-cfe-admin v1.1 — Configuration extensions in a 1C infobase: list, check, properties, delete # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -38,8 +38,15 @@ def ci_parse_args(parser, argv=None): -def _find_project_v8path(): - """Walk up from CWD to find .v8-project.json and read its v8path.""" +def _find_project_v8path(args): + """Walk up from CWD to find .v8-project.json and read its v8path. + + v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + """ + db = find_project_database(args) + if db and db.get("v8path"): + return db["v8path"] d = os.getcwd() while True: pf = os.path.join(d, ".v8-project.json") @@ -88,9 +95,10 @@ V8_SECRET_KEYS = ["/P", "/UC", "/WSP", "/AWSP", "/ConfigurationRepositoryP"] IBCMD_SECRET_KEYS = ["--password", "--token", "--db-pwd"] -# --- Реквизиты хранилища из .v8-project.json --- -# Модель их не передаёт: скрипт сопоставляет параметры соединения с записью в databases[] -# и берёт repository оттуда. Тот же приём, что в cf-edit.py (сопоставление по configSrc). +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.py (сопоставление по configSrc). def _sg_find_v8project(start_dir): d = start_dir for _ in range(20): @@ -329,10 +337,10 @@ def _version_key(p): return [int(x) for x in re.findall(r"\d+", _version_dir(p))] -def resolve_v8path(v8path): +def resolve_v8path(v8path, args): """Resolve path to a 1C executable (1cv8; ibcmd only when given explicitly).""" if not v8path: - v8path = _find_project_v8path() + v8path = _find_project_v8path(args) if not v8path: if os.name == "nt": candidates = ( @@ -629,7 +637,7 @@ def main(): args.InfoBasePath = clean_path(args.InfoBasePath, "-InfoBasePath") assert_infobase_exists(args.InfoBasePath) - v8path = resolve_v8path(args.V8Path) + v8path = resolve_v8path(args.V8Path, args) # --- Утилиты платформы: нужны обе, выбор по команде --- # -V8Path указывает на каталог bin либо на любой из двух файлов; второй берётся соседом. diff --git a/.claude/skills/db-create/SKILL.md b/.claude/skills/db-create/SKILL.md index 388e0f966..23a5b6207 100644 --- a/.claude/skills/db-create/SKILL.md +++ b/.claude/skills/db-create/SKILL.md @@ -24,8 +24,8 @@ allowed-tools: ## Параметры подключения -Прочитай `.v8-project.json` из корня проекта для `v8path` (путь к платформе). -Если `v8path` не задан — скрипт сам попытается определить платформу (`.v8-project.json` → Program Files). +Прочитай `.v8-project.json` из корня проекта. Платформа — `v8path` записи создаваемой базы, +если она уже в реестре, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. После создания базы предложи зарегистрировать через `/db-list add`. ## Команда diff --git a/.claude/skills/db-create/scripts/db-create.ps1 b/.claude/skills/db-create/scripts/db-create.ps1 index ba8a975f9..d827ab385 100644 --- a/.claude/skills/db-create/scripts/db-create.ps1 +++ b/.claude/skills/db-create/scripts/db-create.ps1 @@ -1,4 +1,4 @@ -# db-create v1.14 — Create 1C information base +# db-create v1.15 — Create 1C information base # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -243,8 +243,52 @@ $V8Path = ConvertTo-CleanPath $V8Path '-V8Path' $InfoBasePath = ConvertTo-CleanPath $InfoBasePath '-InfoBasePath' $UseTemplate = ConvertTo-CleanPath $UseTemplate '-UseTemplate' +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.ps1 (сопоставление по configSrc). +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 Test-SamePath { + param([string]$A, [string]$B) + if (-not $A -or -not $B) { return $false } + try { + $na = [System.IO.Path]::GetFullPath($A).TrimEnd('\', '/') + $nb = [System.IO.Path]::GetFullPath($B).TrimEnd('\', '/') + return $na.Equals($nb, [System.StringComparison]::OrdinalIgnoreCase) + } catch { return $false } +} +function Find-ProjectDatabase { + # Запись базы в реестре, соответствующая переданному соединению. $null, если не найдена. + $pf = Find-V8Project (Get-Location).Path + if (-not $pf) { return $null } + try { $proj = Get-Content $pf -Raw -Encoding UTF8 | ConvertFrom-Json } catch { return $null } + if (-not $proj.databases) { return $null } + foreach ($db in $proj.databases) { + if ($InfoBasePath -and $db.path -and (Test-SamePath $db.path $InfoBasePath)) { return $db } + if ($InfoBaseServer -and $InfoBaseRef -and $db.server -and $db.ref) { + if ($db.server.Equals($InfoBaseServer, [System.StringComparison]::OrdinalIgnoreCase) -and + $db.ref.Equals($InfoBaseRef, [System.StringComparison]::OrdinalIgnoreCase)) { return $db } + } + } + return $null +} + # --- Resolve V8Path --- function Find-ProjectV8Path { + # v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + # платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + $dbRec = Find-ProjectDatabase + if ($dbRec -and $dbRec.v8path) { return [string]$dbRec.v8path } $dir = (Get-Location).Path while ($dir) { $pf = Join-Path $dir ".v8-project.json" diff --git a/.claude/skills/db-create/scripts/db-create.py b/.claude/skills/db-create/scripts/db-create.py index 69a8fe495..c51821715 100644 --- a/.claude/skills/db-create/scripts/db-create.py +++ b/.claude/skills/db-create/scripts/db-create.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-create v1.14 — Create 1C information base +# db-create v1.15 — Create 1C information base # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -37,8 +37,62 @@ def ci_parse_args(parser, argv=None): -def _find_project_v8path(): - """Walk up from CWD to find .v8-project.json and read its v8path.""" +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.py (сопоставление по configSrc). +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 same_path(a, b): + if not a or not b: + return False + try: + return os.path.abspath(a).rstrip("\\/").lower() == os.path.abspath(b).rstrip("\\/").lower() + except Exception: + return False + + +def find_project_database(args): + """Запись базы в реестре, соответствующая переданному соединению. None, если не найдена.""" + pf = _sg_find_v8project(os.getcwd()) + if not pf: + return None + try: + with open(pf, encoding="utf-8-sig") as f: + proj = json.load(f) + except Exception: + return None + for db in proj.get("databases") or []: + if args.InfoBasePath and db.get("path") and same_path(db["path"], args.InfoBasePath): + return db + if args.InfoBaseServer and args.InfoBaseRef and db.get("server") and db.get("ref"): + if (db["server"].lower() == args.InfoBaseServer.lower() + and db["ref"].lower() == args.InfoBaseRef.lower()): + return db + return None + + +def _find_project_v8path(args): + """Walk up from CWD to find .v8-project.json and read its v8path. + + v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + """ + db = find_project_database(args) + if db and db.get("v8path"): + return db["v8path"] d = os.getcwd() while True: pf = os.path.join(d, ".v8-project.json") @@ -243,10 +297,10 @@ def _version_key(p): return [int(x) for x in re.findall(r"\d+", _version_dir(p))] -def resolve_v8path(v8path): +def resolve_v8path(v8path, args): """Resolve path to a 1C executable (1cv8; ibcmd only when given explicitly).""" if not v8path: - v8path = _find_project_v8path() + v8path = _find_project_v8path(args) if not v8path: if os.name == "nt": candidates = ( @@ -418,7 +472,7 @@ def main(): args.InfoBasePath = clean_path(args.InfoBasePath, "-InfoBasePath") args.UseTemplate = clean_path(args.UseTemplate, "-UseTemplate") - v8path = resolve_v8path(args.V8Path) + v8path = resolve_v8path(args.V8Path, args) engine = "ibcmd" if os.path.basename(v8path).lower().startswith("ibcmd") else "1cv8" # --- Resolve additional arguments for the selected engine --- diff --git a/.claude/skills/db-dump-cf/SKILL.md b/.claude/skills/db-dump-cf/SKILL.md index c56ee02aa..19ca5ce15 100644 --- a/.claude/skills/db-dump-cf/SKILL.md +++ b/.claude/skills/db-dump-cf/SKILL.md @@ -23,12 +23,12 @@ allowed-tools: ## Параметры подключения -Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` (путь к платформе) и разреши базу: +Прочитай `.v8-project.json` из корня проекта и разреши базу: 1. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую 2. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` 3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` 4. Если ветка не совпала — используй `default` -Если `v8path` не задан — скрипт сам попытается определить платформу (`.v8-project.json` → Program Files). +Платформа — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. Если файла нет — предложи `/db-list add`. Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. diff --git a/.claude/skills/db-dump-cf/scripts/db-dump-cf.ps1 b/.claude/skills/db-dump-cf/scripts/db-dump-cf.ps1 index 453e0b454..2028f8efb 100644 --- a/.claude/skills/db-dump-cf/scripts/db-dump-cf.ps1 +++ b/.claude/skills/db-dump-cf/scripts/db-dump-cf.ps1 @@ -1,4 +1,4 @@ -# db-dump-cf v1.16 — Dump 1C configuration to CF file +# db-dump-cf v1.17 — Dump 1C configuration to CF file # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -272,8 +272,52 @@ function Assert-InfoBaseExists { Assert-InfoBaseExists $InfoBasePath +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.ps1 (сопоставление по configSrc). +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 Test-SamePath { + param([string]$A, [string]$B) + if (-not $A -or -not $B) { return $false } + try { + $na = [System.IO.Path]::GetFullPath($A).TrimEnd('\', '/') + $nb = [System.IO.Path]::GetFullPath($B).TrimEnd('\', '/') + return $na.Equals($nb, [System.StringComparison]::OrdinalIgnoreCase) + } catch { return $false } +} +function Find-ProjectDatabase { + # Запись базы в реестре, соответствующая переданному соединению. $null, если не найдена. + $pf = Find-V8Project (Get-Location).Path + if (-not $pf) { return $null } + try { $proj = Get-Content $pf -Raw -Encoding UTF8 | ConvertFrom-Json } catch { return $null } + if (-not $proj.databases) { return $null } + foreach ($db in $proj.databases) { + if ($InfoBasePath -and $db.path -and (Test-SamePath $db.path $InfoBasePath)) { return $db } + if ($InfoBaseServer -and $InfoBaseRef -and $db.server -and $db.ref) { + if ($db.server.Equals($InfoBaseServer, [System.StringComparison]::OrdinalIgnoreCase) -and + $db.ref.Equals($InfoBaseRef, [System.StringComparison]::OrdinalIgnoreCase)) { return $db } + } + } + return $null +} + # --- Resolve V8Path --- function Find-ProjectV8Path { + # v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + # платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + $dbRec = Find-ProjectDatabase + if ($dbRec -and $dbRec.v8path) { return [string]$dbRec.v8path } $dir = (Get-Location).Path while ($dir) { $pf = Join-Path $dir ".v8-project.json" diff --git a/.claude/skills/db-dump-cf/scripts/db-dump-cf.py b/.claude/skills/db-dump-cf/scripts/db-dump-cf.py index 1bcb57620..2b74e872e 100644 --- a/.claude/skills/db-dump-cf/scripts/db-dump-cf.py +++ b/.claude/skills/db-dump-cf/scripts/db-dump-cf.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-dump-cf v1.16 — Dump 1C configuration to CF file +# db-dump-cf v1.17 — Dump 1C configuration to CF file # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -37,8 +37,62 @@ def ci_parse_args(parser, argv=None): -def _find_project_v8path(): - """Walk up from CWD to find .v8-project.json and read its v8path.""" +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.py (сопоставление по configSrc). +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 same_path(a, b): + if not a or not b: + return False + try: + return os.path.abspath(a).rstrip("\\/").lower() == os.path.abspath(b).rstrip("\\/").lower() + except Exception: + return False + + +def find_project_database(args): + """Запись базы в реестре, соответствующая переданному соединению. None, если не найдена.""" + pf = _sg_find_v8project(os.getcwd()) + if not pf: + return None + try: + with open(pf, encoding="utf-8-sig") as f: + proj = json.load(f) + except Exception: + return None + for db in proj.get("databases") or []: + if args.InfoBasePath and db.get("path") and same_path(db["path"], args.InfoBasePath): + return db + if args.InfoBaseServer and args.InfoBaseRef and db.get("server") and db.get("ref"): + if (db["server"].lower() == args.InfoBaseServer.lower() + and db["ref"].lower() == args.InfoBaseRef.lower()): + return db + return None + + +def _find_project_v8path(args): + """Walk up from CWD to find .v8-project.json and read its v8path. + + v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + """ + db = find_project_database(args) + if db and db.get("v8path"): + return db["v8path"] d = os.getcwd() while True: pf = os.path.join(d, ".v8-project.json") @@ -243,10 +297,10 @@ def _version_key(p): return [int(x) for x in re.findall(r"\d+", _version_dir(p))] -def resolve_v8path(v8path): +def resolve_v8path(v8path, args): """Resolve path to a 1C executable (1cv8; ibcmd only when given explicitly).""" if not v8path: - v8path = _find_project_v8path() + v8path = _find_project_v8path(args) if not v8path: if os.name == "nt": candidates = ( @@ -439,7 +493,7 @@ def main(): assert_infobase_exists(args.InfoBasePath) args.OutputFile = clean_path(args.OutputFile, "-OutputFile") - v8path = resolve_v8path(args.V8Path) + v8path = resolve_v8path(args.V8Path, args) engine = "ibcmd" if os.path.basename(v8path).lower().startswith("ibcmd") else "1cv8" # --- Resolve additional arguments for the selected engine --- diff --git a/.claude/skills/db-dump-dt/SKILL.md b/.claude/skills/db-dump-dt/SKILL.md index aef935ec6..6d6c4a63f 100644 --- a/.claude/skills/db-dump-dt/SKILL.md +++ b/.claude/skills/db-dump-dt/SKILL.md @@ -26,12 +26,12 @@ allowed-tools: ## Параметры подключения -Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` (путь к платформе) и разреши базу: +Прочитай `.v8-project.json` из корня проекта и разреши базу: 1. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую 2. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` 3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` 4. Если ветка не совпала — используй `default` -Если `v8path` не задан — скрипт сам попытается определить платформу (`.v8-project.json` → Program Files). +Платформа — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. Если файла нет — предложи `/db-list add`. Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. diff --git a/.claude/skills/db-dump-dt/scripts/db-dump-dt.ps1 b/.claude/skills/db-dump-dt/scripts/db-dump-dt.ps1 index 077ecf770..c2d720920 100644 --- a/.claude/skills/db-dump-dt/scripts/db-dump-dt.ps1 +++ b/.claude/skills/db-dump-dt/scripts/db-dump-dt.ps1 @@ -1,4 +1,4 @@ -# db-dump-dt v1.15 — Dump 1C information base to DT file +# db-dump-dt v1.16 — Dump 1C information base to DT file # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -256,8 +256,52 @@ function Assert-InfoBaseExists { Assert-InfoBaseExists $InfoBasePath +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.ps1 (сопоставление по configSrc). +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 Test-SamePath { + param([string]$A, [string]$B) + if (-not $A -or -not $B) { return $false } + try { + $na = [System.IO.Path]::GetFullPath($A).TrimEnd('\', '/') + $nb = [System.IO.Path]::GetFullPath($B).TrimEnd('\', '/') + return $na.Equals($nb, [System.StringComparison]::OrdinalIgnoreCase) + } catch { return $false } +} +function Find-ProjectDatabase { + # Запись базы в реестре, соответствующая переданному соединению. $null, если не найдена. + $pf = Find-V8Project (Get-Location).Path + if (-not $pf) { return $null } + try { $proj = Get-Content $pf -Raw -Encoding UTF8 | ConvertFrom-Json } catch { return $null } + if (-not $proj.databases) { return $null } + foreach ($db in $proj.databases) { + if ($InfoBasePath -and $db.path -and (Test-SamePath $db.path $InfoBasePath)) { return $db } + if ($InfoBaseServer -and $InfoBaseRef -and $db.server -and $db.ref) { + if ($db.server.Equals($InfoBaseServer, [System.StringComparison]::OrdinalIgnoreCase) -and + $db.ref.Equals($InfoBaseRef, [System.StringComparison]::OrdinalIgnoreCase)) { return $db } + } + } + return $null +} + # --- Resolve V8Path --- function Find-ProjectV8Path { + # v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + # платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + $dbRec = Find-ProjectDatabase + if ($dbRec -and $dbRec.v8path) { return [string]$dbRec.v8path } $dir = (Get-Location).Path while ($dir) { $pf = Join-Path $dir ".v8-project.json" diff --git a/.claude/skills/db-dump-dt/scripts/db-dump-dt.py b/.claude/skills/db-dump-dt/scripts/db-dump-dt.py index 0380e4d32..72aa08260 100644 --- a/.claude/skills/db-dump-dt/scripts/db-dump-dt.py +++ b/.claude/skills/db-dump-dt/scripts/db-dump-dt.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-dump-dt v1.15 — Dump 1C information base to DT file +# db-dump-dt v1.16 — Dump 1C information base to DT file # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -37,8 +37,62 @@ def ci_parse_args(parser, argv=None): -def _find_project_v8path(): - """Walk up from CWD to find .v8-project.json and read its v8path.""" +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.py (сопоставление по configSrc). +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 same_path(a, b): + if not a or not b: + return False + try: + return os.path.abspath(a).rstrip("\\/").lower() == os.path.abspath(b).rstrip("\\/").lower() + except Exception: + return False + + +def find_project_database(args): + """Запись базы в реестре, соответствующая переданному соединению. None, если не найдена.""" + pf = _sg_find_v8project(os.getcwd()) + if not pf: + return None + try: + with open(pf, encoding="utf-8-sig") as f: + proj = json.load(f) + except Exception: + return None + for db in proj.get("databases") or []: + if args.InfoBasePath and db.get("path") and same_path(db["path"], args.InfoBasePath): + return db + if args.InfoBaseServer and args.InfoBaseRef and db.get("server") and db.get("ref"): + if (db["server"].lower() == args.InfoBaseServer.lower() + and db["ref"].lower() == args.InfoBaseRef.lower()): + return db + return None + + +def _find_project_v8path(args): + """Walk up from CWD to find .v8-project.json and read its v8path. + + v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + """ + db = find_project_database(args) + if db and db.get("v8path"): + return db["v8path"] d = os.getcwd() while True: pf = os.path.join(d, ".v8-project.json") @@ -243,10 +297,10 @@ def _version_key(p): return [int(x) for x in re.findall(r"\d+", _version_dir(p))] -def resolve_v8path(v8path): +def resolve_v8path(v8path, args): """Resolve path to a 1C executable (1cv8; ibcmd only when given explicitly).""" if not v8path: - v8path = _find_project_v8path() + v8path = _find_project_v8path(args) if not v8path: if os.name == "nt": candidates = ( @@ -437,7 +491,7 @@ def main(): assert_infobase_exists(args.InfoBasePath) args.OutputFile = clean_path(args.OutputFile, "-OutputFile") - v8path = resolve_v8path(args.V8Path) + v8path = resolve_v8path(args.V8Path, args) engine = "ibcmd" if os.path.basename(v8path).lower().startswith("ibcmd") else "1cv8" # --- Resolve additional arguments for the selected engine --- diff --git a/.claude/skills/db-dump-xml/SKILL.md b/.claude/skills/db-dump-xml/SKILL.md index 8d0ce6e31..42aa0630e 100644 --- a/.claude/skills/db-dump-xml/SKILL.md +++ b/.claude/skills/db-dump-xml/SKILL.md @@ -24,12 +24,12 @@ allowed-tools: ## Параметры подключения -Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` (путь к платформе) и разреши базу: +Прочитай `.v8-project.json` из корня проекта и разреши базу: 1. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую 2. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` 3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` 4. Если ветка не совпала — используй `default` -Если `v8path` не задан — скрипт сам попытается определить платформу (`.v8-project.json` → Program Files). +Платформа — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. Если файла нет — предложи `/db-list add`. Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. Если в записи базы указан `configSrc` — используй как каталог выгрузки по умолчанию. diff --git a/.claude/skills/db-dump-xml/scripts/db-dump-xml.ps1 b/.claude/skills/db-dump-xml/scripts/db-dump-xml.ps1 index b93819098..9560cae62 100644 --- a/.claude/skills/db-dump-xml/scripts/db-dump-xml.ps1 +++ b/.claude/skills/db-dump-xml/scripts/db-dump-xml.ps1 @@ -1,4 +1,4 @@ -# db-dump-xml v1.21 — Dump 1C configuration to XML files +# db-dump-xml v1.22 — Dump 1C configuration to XML files # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -125,9 +125,10 @@ param( $OutputEncoding = [System.Text.Encoding]::UTF8 [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 -# --- Реквизиты хранилища из .v8-project.json --- -# Модель их не передаёт: скрипт сопоставляет параметры соединения с записью в databases[] -# и берёт repository оттуда. Тот же приём, что в cf-edit.ps1 (сопоставление по configSrc). +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.ps1 (сопоставление по configSrc). function Find-V8Project([string]$startDir) { $d = $startDir for ($i = 0; $i -lt 20 -and $d; $i++) { @@ -395,6 +396,10 @@ Assert-InfoBaseExists $InfoBasePath # --- Resolve V8Path --- function Find-ProjectV8Path { + # v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + # платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + $dbRec = Find-ProjectDatabase + if ($dbRec -and $dbRec.v8path) { return [string]$dbRec.v8path } $dir = (Get-Location).Path while ($dir) { $pf = Join-Path $dir ".v8-project.json" diff --git a/.claude/skills/db-dump-xml/scripts/db-dump-xml.py b/.claude/skills/db-dump-xml/scripts/db-dump-xml.py index a20e3e3e6..1b45b8781 100644 --- a/.claude/skills/db-dump-xml/scripts/db-dump-xml.py +++ b/.claude/skills/db-dump-xml/scripts/db-dump-xml.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-dump-xml v1.21 — Dump 1C configuration to XML files +# db-dump-xml v1.22 — Dump 1C configuration to XML files # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -37,8 +37,15 @@ def ci_parse_args(parser, argv=None): -def _find_project_v8path(): - """Walk up from CWD to find .v8-project.json and read its v8path.""" +def _find_project_v8path(args): + """Walk up from CWD to find .v8-project.json and read its v8path. + + v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + """ + db = find_project_database(args) + if db and db.get("v8path"): + return db["v8path"] d = os.getcwd() while True: pf = os.path.join(d, ".v8-project.json") @@ -87,9 +94,10 @@ V8_SECRET_KEYS = ["/P", "/UC", "/WSP", "/AWSP", "/ConfigurationRepositoryP"] IBCMD_SECRET_KEYS = ["--password", "--token", "--db-pwd"] -# --- Реквизиты хранилища из .v8-project.json --- -# Модель их не передаёт: скрипт сопоставляет параметры соединения с записью в databases[] -# и берёт repository оттуда. Тот же приём, что в cf-edit.py (сопоставление по configSrc). +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.py (сопоставление по configSrc). def _sg_find_v8project(start_dir): d = start_dir for _ in range(20): @@ -328,10 +336,10 @@ def _version_key(p): return [int(x) for x in re.findall(r"\d+", _version_dir(p))] -def resolve_v8path(v8path): +def resolve_v8path(v8path, args): """Resolve path to a 1C executable (1cv8; ibcmd only when given explicitly).""" if not v8path: - v8path = _find_project_v8path() + v8path = _find_project_v8path(args) if not v8path: if os.name == "nt": candidates = ( @@ -542,7 +550,7 @@ def main(): args.ConfigDir = clean_path(args.ConfigDir, "-ConfigDir") # --- Resolve V8Path --- - v8path = resolve_v8path(args.V8Path) + v8path = resolve_v8path(args.V8Path, args) engine = "ibcmd" if os.path.basename(v8path).lower().startswith("ibcmd") else "1cv8" # --- Resolve additional arguments for the selected engine --- diff --git a/.claude/skills/db-list/SKILL.md b/.claude/skills/db-list/SKILL.md index aedc213ec..b8921d102 100644 --- a/.claude/skills/db-list/SKILL.md +++ b/.claude/skills/db-list/SKILL.md @@ -36,6 +36,7 @@ allowed-tools: "name": "Разработка", "type": "file", "path": "C:\\Bases\\MyApp_Dev", + "v8path": "C:\\Program Files\\1cv8\\8.3.27.1688\\bin", "user": "Admin", "password": "", "aliases": ["dev", "разработка"], @@ -92,6 +93,7 @@ allowed-tools: | `path` | string | для file | Путь к каталогу файловой базы | | `server` | string | для server | Адрес сервера 1С | | `ref` | string | для server | Имя базы на сервере | +| `v8path` | string | нет | Платформа именно этой базы — перекрывает корневой `v8path` (см. ниже) | | `user` | string | нет | Имя пользователя 1С | | `password` | string | нет | Пароль | | `aliases` | string[] | нет | Альтернативные имена для быстрого доступа | @@ -145,9 +147,15 @@ allowed-tools: После выполнения: если использованная база не зарегистрирована — предложи добавить через `/db-list add`. -### Автоопределение платформы +### Выбор платформы -Если `v8path` не задан в конфиге: +Порядок: явный `-V8Path` в вызове → `v8path` записи базы → корневой `v8path` → автоопределение. +Запись базы находит сам скрипт, сопоставляя параметры соединения с `databases[]`, — передавать +`-V8Path` руками не нужно. Поле у базы нужно, когда базы проекта живут на разных версиях платформы: +формат XML-выгрузки задаёт та платформа, которая выгружает, а автоопределение берёт самую старшую +установленную. + +Если `v8path` не задан нигде: ```powershell $v8 = Get-ChildItem "C:\Program Files\1cv8\*\bin\1cv8.exe" | Sort-Object -Descending | Select-Object -First 1 diff --git a/.claude/skills/db-load-cf/SKILL.md b/.claude/skills/db-load-cf/SKILL.md index d9ba9766e..d8c4cf5c9 100644 --- a/.claude/skills/db-load-cf/SKILL.md +++ b/.claude/skills/db-load-cf/SKILL.md @@ -24,12 +24,12 @@ allowed-tools: ## Параметры подключения -Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` (путь к платформе) и разреши базу: +Прочитай `.v8-project.json` из корня проекта и разреши базу: 1. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую 2. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` 3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` 4. Если ветка не совпала — используй `default` -Если `v8path` не задан — скрипт сам попытается определить платформу (`.v8-project.json` → Program Files). +Платформа — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. Если файла нет — предложи `/db-list add`. Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. diff --git a/.claude/skills/db-load-cf/scripts/db-load-cf.ps1 b/.claude/skills/db-load-cf/scripts/db-load-cf.ps1 index ce6b98e4a..3ea0fb637 100644 --- a/.claude/skills/db-load-cf/scripts/db-load-cf.ps1 +++ b/.claude/skills/db-load-cf/scripts/db-load-cf.ps1 @@ -1,4 +1,4 @@ -# db-load-cf v1.17 — Load 1C configuration from CF file +# db-load-cf v1.18 — Load 1C configuration from CF file # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -299,8 +299,41 @@ function Assert-InfoBaseExists { Assert-InfoBaseExists $InfoBasePath +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.ps1 (сопоставление по configSrc). +function Test-SamePath { + param([string]$A, [string]$B) + if (-not $A -or -not $B) { return $false } + try { + $na = [System.IO.Path]::GetFullPath($A).TrimEnd('\', '/') + $nb = [System.IO.Path]::GetFullPath($B).TrimEnd('\', '/') + return $na.Equals($nb, [System.StringComparison]::OrdinalIgnoreCase) + } catch { return $false } +} +function Find-ProjectDatabase { + # Запись базы в реестре, соответствующая переданному соединению. $null, если не найдена. + $pf = Find-V8Project (Get-Location).Path + if (-not $pf) { return $null } + try { $proj = Get-Content $pf -Raw -Encoding UTF8 | ConvertFrom-Json } catch { return $null } + if (-not $proj.databases) { return $null } + foreach ($db in $proj.databases) { + if ($InfoBasePath -and $db.path -and (Test-SamePath $db.path $InfoBasePath)) { return $db } + if ($InfoBaseServer -and $InfoBaseRef -and $db.server -and $db.ref) { + if ($db.server.Equals($InfoBaseServer, [System.StringComparison]::OrdinalIgnoreCase) -and + $db.ref.Equals($InfoBaseRef, [System.StringComparison]::OrdinalIgnoreCase)) { return $db } + } + } + return $null +} + # --- Resolve V8Path --- function Find-ProjectV8Path { + # v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + # платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + $dbRec = Find-ProjectDatabase + if ($dbRec -and $dbRec.v8path) { return [string]$dbRec.v8path } $dir = (Get-Location).Path while ($dir) { $pf = Join-Path $dir ".v8-project.json" diff --git a/.claude/skills/db-load-cf/scripts/db-load-cf.py b/.claude/skills/db-load-cf/scripts/db-load-cf.py index 888e72911..069aa8efd 100644 --- a/.claude/skills/db-load-cf/scripts/db-load-cf.py +++ b/.claude/skills/db-load-cf/scripts/db-load-cf.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-load-cf v1.17 — Load 1C configuration from CF file +# db-load-cf v1.18 — Load 1C configuration from CF file # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -37,8 +37,48 @@ def ci_parse_args(parser, argv=None): -def _find_project_v8path(): - """Walk up from CWD to find .v8-project.json and read its v8path.""" +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.py (сопоставление по configSrc). +def same_path(a, b): + if not a or not b: + return False + try: + return os.path.abspath(a).rstrip("\\/").lower() == os.path.abspath(b).rstrip("\\/").lower() + except Exception: + return False + + +def find_project_database(args): + """Запись базы в реестре, соответствующая переданному соединению. None, если не найдена.""" + pf = _sg_find_v8project(os.getcwd()) + if not pf: + return None + try: + with open(pf, encoding="utf-8-sig") as f: + proj = json.load(f) + except Exception: + return None + for db in proj.get("databases") or []: + if args.InfoBasePath and db.get("path") and same_path(db["path"], args.InfoBasePath): + return db + if args.InfoBaseServer and args.InfoBaseRef and db.get("server") and db.get("ref"): + if (db["server"].lower() == args.InfoBaseServer.lower() + and db["ref"].lower() == args.InfoBaseRef.lower()): + return db + return None + + +def _find_project_v8path(args): + """Walk up from CWD to find .v8-project.json and read its v8path. + + v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + """ + db = find_project_database(args) + if db and db.get("v8path"): + return db["v8path"] d = os.getcwd() while True: pf = os.path.join(d, ".v8-project.json") @@ -243,10 +283,10 @@ def _version_key(p): return [int(x) for x in re.findall(r"\d+", _version_dir(p))] -def resolve_v8path(v8path): +def resolve_v8path(v8path, args): """Resolve path to a 1C executable (1cv8; ibcmd only when given explicitly).""" if not v8path: - v8path = _find_project_v8path() + v8path = _find_project_v8path(args) if not v8path: if os.name == "nt": candidates = ( @@ -548,7 +588,7 @@ def main(): assert_infobase_exists(args.InfoBasePath) args.InputFile = clean_path(args.InputFile, "-InputFile") - v8path = resolve_v8path(args.V8Path) + v8path = resolve_v8path(args.V8Path, args) engine = "ibcmd" if os.path.basename(v8path).lower().startswith("ibcmd") else "1cv8" # --- Resolve additional arguments for the selected engine --- diff --git a/.claude/skills/db-load-dt/SKILL.md b/.claude/skills/db-load-dt/SKILL.md index a55023031..132204b72 100644 --- a/.claude/skills/db-load-dt/SKILL.md +++ b/.claude/skills/db-load-dt/SKILL.md @@ -40,12 +40,12 @@ allowed-tools: ## Параметры подключения -Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` (путь к платформе) и разреши базу: +Прочитай `.v8-project.json` из корня проекта и разреши базу: 1. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую 2. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` 3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` 4. Если ветка не совпала — используй `default` -Если `v8path` не задан — скрипт сам попытается определить платформу (`.v8-project.json` → Program Files). +Платформа — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. Если файла нет — предложи `/db-list add`. Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. diff --git a/.claude/skills/db-load-dt/scripts/db-load-dt.ps1 b/.claude/skills/db-load-dt/scripts/db-load-dt.ps1 index bbbbf735f..50a91df6a 100644 --- a/.claude/skills/db-load-dt/scripts/db-load-dt.ps1 +++ b/.claude/skills/db-load-dt/scripts/db-load-dt.ps1 @@ -1,4 +1,4 @@ -# db-load-dt v1.16 — Load 1C information base from DT file +# db-load-dt v1.17 — Load 1C information base from DT file # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -286,8 +286,52 @@ function Assert-InfoBaseExists { Assert-InfoBaseExists $InfoBasePath +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.ps1 (сопоставление по configSrc). +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 Test-SamePath { + param([string]$A, [string]$B) + if (-not $A -or -not $B) { return $false } + try { + $na = [System.IO.Path]::GetFullPath($A).TrimEnd('\', '/') + $nb = [System.IO.Path]::GetFullPath($B).TrimEnd('\', '/') + return $na.Equals($nb, [System.StringComparison]::OrdinalIgnoreCase) + } catch { return $false } +} +function Find-ProjectDatabase { + # Запись базы в реестре, соответствующая переданному соединению. $null, если не найдена. + $pf = Find-V8Project (Get-Location).Path + if (-not $pf) { return $null } + try { $proj = Get-Content $pf -Raw -Encoding UTF8 | ConvertFrom-Json } catch { return $null } + if (-not $proj.databases) { return $null } + foreach ($db in $proj.databases) { + if ($InfoBasePath -and $db.path -and (Test-SamePath $db.path $InfoBasePath)) { return $db } + if ($InfoBaseServer -and $InfoBaseRef -and $db.server -and $db.ref) { + if ($db.server.Equals($InfoBaseServer, [System.StringComparison]::OrdinalIgnoreCase) -and + $db.ref.Equals($InfoBaseRef, [System.StringComparison]::OrdinalIgnoreCase)) { return $db } + } + } + return $null +} + # --- Resolve V8Path --- function Find-ProjectV8Path { + # v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + # платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + $dbRec = Find-ProjectDatabase + if ($dbRec -and $dbRec.v8path) { return [string]$dbRec.v8path } $dir = (Get-Location).Path while ($dir) { $pf = Join-Path $dir ".v8-project.json" diff --git a/.claude/skills/db-load-dt/scripts/db-load-dt.py b/.claude/skills/db-load-dt/scripts/db-load-dt.py index a85a45f36..c97447bfd 100644 --- a/.claude/skills/db-load-dt/scripts/db-load-dt.py +++ b/.claude/skills/db-load-dt/scripts/db-load-dt.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-load-dt v1.16 — Load 1C information base from DT file +# db-load-dt v1.17 — Load 1C information base from DT file # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -37,8 +37,62 @@ def ci_parse_args(parser, argv=None): -def _find_project_v8path(): - """Walk up from CWD to find .v8-project.json and read its v8path.""" +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.py (сопоставление по configSrc). +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 same_path(a, b): + if not a or not b: + return False + try: + return os.path.abspath(a).rstrip("\\/").lower() == os.path.abspath(b).rstrip("\\/").lower() + except Exception: + return False + + +def find_project_database(args): + """Запись базы в реестре, соответствующая переданному соединению. None, если не найдена.""" + pf = _sg_find_v8project(os.getcwd()) + if not pf: + return None + try: + with open(pf, encoding="utf-8-sig") as f: + proj = json.load(f) + except Exception: + return None + for db in proj.get("databases") or []: + if args.InfoBasePath and db.get("path") and same_path(db["path"], args.InfoBasePath): + return db + if args.InfoBaseServer and args.InfoBaseRef and db.get("server") and db.get("ref"): + if (db["server"].lower() == args.InfoBaseServer.lower() + and db["ref"].lower() == args.InfoBaseRef.lower()): + return db + return None + + +def _find_project_v8path(args): + """Walk up from CWD to find .v8-project.json and read its v8path. + + v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + """ + db = find_project_database(args) + if db and db.get("v8path"): + return db["v8path"] d = os.getcwd() while True: pf = os.path.join(d, ".v8-project.json") @@ -243,10 +297,10 @@ def _version_key(p): return [int(x) for x in re.findall(r"\d+", _version_dir(p))] -def resolve_v8path(v8path): +def resolve_v8path(v8path, args): """Resolve path to a 1C executable (1cv8; ibcmd only when given explicitly).""" if not v8path: - v8path = _find_project_v8path() + v8path = _find_project_v8path(args) if not v8path: if os.name == "nt": candidates = ( @@ -457,7 +511,7 @@ def main(): assert_infobase_exists(args.InfoBasePath) args.InputFile = clean_path(args.InputFile, "-InputFile") - v8path = resolve_v8path(args.V8Path) + v8path = resolve_v8path(args.V8Path, args) engine = "ibcmd" if os.path.basename(v8path).lower().startswith("ibcmd") else "1cv8" # --- Resolve additional arguments for the selected engine --- diff --git a/.claude/skills/db-load-git/SKILL.md b/.claude/skills/db-load-git/SKILL.md index 20c2e678e..9891d54b7 100644 --- a/.claude/skills/db-load-git/SKILL.md +++ b/.claude/skills/db-load-git/SKILL.md @@ -25,12 +25,12 @@ allowed-tools: ## Параметры подключения -Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` (путь к платформе) и разреши базу: +Прочитай `.v8-project.json` из корня проекта и разреши базу: 1. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую 2. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` 3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` 4. Если ветка не совпала — используй `default` -Если `v8path` не задан — скрипт сам попытается определить платформу (`.v8-project.json` → Program Files). +Платформа — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. Если файла нет — предложи `/db-list add`. Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. Если в записи базы указан `configSrc` — используй как каталог конфигурации. diff --git a/.claude/skills/db-load-git/scripts/db-load-git.ps1 b/.claude/skills/db-load-git/scripts/db-load-git.ps1 index 49c64ad36..c36159277 100644 --- a/.claude/skills/db-load-git/scripts/db-load-git.ps1 +++ b/.claude/skills/db-load-git/scripts/db-load-git.ps1 @@ -1,4 +1,4 @@ -# db-load-git v1.26 — Load Git changes into 1C database +# db-load-git v1.27 — Load Git changes into 1C database # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -139,9 +139,10 @@ param( $OutputEncoding = [System.Text.Encoding]::UTF8 [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 -# --- Реквизиты хранилища из .v8-project.json --- -# Модель их не передаёт: скрипт сопоставляет параметры соединения с записью в databases[] -# и берёт repository оттуда. Тот же приём, что в cf-edit.ps1 (сопоставление по configSrc). +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.ps1 (сопоставление по configSrc). function Find-V8Project([string]$startDir) { $d = $startDir for ($i = 0; $i -lt 20 -and $d; $i++) { @@ -427,6 +428,10 @@ function Get-ObjectXmlFromSubFile { # --- Resolve V8Path (skip if DryRun) --- if (-not $DryRun) { function Find-ProjectV8Path { + # v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + # платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + $dbRec = Find-ProjectDatabase + if ($dbRec -and $dbRec.v8path) { return [string]$dbRec.v8path } $dir = (Get-Location).Path while ($dir) { $pf = Join-Path $dir ".v8-project.json" diff --git a/.claude/skills/db-load-git/scripts/db-load-git.py b/.claude/skills/db-load-git/scripts/db-load-git.py index 9a835b482..e7faff8d8 100644 --- a/.claude/skills/db-load-git/scripts/db-load-git.py +++ b/.claude/skills/db-load-git/scripts/db-load-git.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-load-git v1.26 — Load Git changes into 1C database +# db-load-git v1.27 — Load Git changes into 1C database # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -37,8 +37,15 @@ def ci_parse_args(parser, argv=None): -def _find_project_v8path(): - """Walk up from CWD to find .v8-project.json and read its v8path.""" +def _find_project_v8path(args): + """Walk up from CWD to find .v8-project.json and read its v8path. + + v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + """ + db = find_project_database(args) + if db and db.get("v8path"): + return db["v8path"] d = os.getcwd() while True: pf = os.path.join(d, ".v8-project.json") @@ -87,9 +94,10 @@ V8_SECRET_KEYS = ["/P", "/UC", "/WSP", "/AWSP", "/ConfigurationRepositoryP"] IBCMD_SECRET_KEYS = ["--password", "--token", "--db-pwd"] -# --- Реквизиты хранилища из .v8-project.json --- -# Модель их не передаёт: скрипт сопоставляет параметры соединения с записью в databases[] -# и берёт repository оттуда. Тот же приём, что в cf-edit.py (сопоставление по configSrc). +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.py (сопоставление по configSrc). def _sg_find_v8project(start_dir): d = start_dir for _ in range(20): @@ -349,10 +357,10 @@ def _version_key(p): return [int(x) for x in re.findall(r"\d+", _version_dir(p))] -def resolve_v8path(v8path): +def resolve_v8path(v8path, args): """Resolve path to a 1C executable (1cv8; ibcmd only when given explicitly).""" if not v8path: - v8path = _find_project_v8path() + v8path = _find_project_v8path(args) if not v8path: if os.name == "nt": candidates = ( @@ -714,7 +722,7 @@ def main(): # --- Resolve V8Path (skip if DryRun) --- v8path = None if not args.DryRun: - v8path = resolve_v8path(args.V8Path) + v8path = resolve_v8path(args.V8Path, args) # --- Detect engine + validate connection (skip if DryRun) --- engine = "1cv8" diff --git a/.claude/skills/db-load-xml/SKILL.md b/.claude/skills/db-load-xml/SKILL.md index b7c13335f..77db67970 100644 --- a/.claude/skills/db-load-xml/SKILL.md +++ b/.claude/skills/db-load-xml/SKILL.md @@ -25,12 +25,12 @@ allowed-tools: ## Параметры подключения -Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` (путь к платформе) и разреши базу: +Прочитай `.v8-project.json` из корня проекта и разреши базу: 1. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую 2. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` 3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` 4. Если ветка не совпала — используй `default` -Если `v8path` не задан — скрипт сам попытается определить платформу (`.v8-project.json` → Program Files). +Платформа — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. Если файла нет — предложи `/db-list add`. Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. Если в записи базы указан `configSrc` — используй как каталог загрузки по умолчанию. diff --git a/.claude/skills/db-load-xml/scripts/db-load-xml.ps1 b/.claude/skills/db-load-xml/scripts/db-load-xml.ps1 index daaa9eee6..08cec3d10 100644 --- a/.claude/skills/db-load-xml/scripts/db-load-xml.ps1 +++ b/.claude/skills/db-load-xml/scripts/db-load-xml.ps1 @@ -1,4 +1,4 @@ -# db-load-xml v1.28 — Load 1C configuration from XML files +# db-load-xml v1.29 — Load 1C configuration from XML files # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -135,9 +135,10 @@ param( $OutputEncoding = [System.Text.Encoding]::UTF8 [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 -# --- Реквизиты хранилища из .v8-project.json --- -# Модель их не передаёт: скрипт сопоставляет параметры соединения с записью в databases[] -# и берёт repository оттуда. Тот же приём, что в cf-edit.ps1 (сопоставление по configSrc). +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.ps1 (сопоставление по configSrc). function Find-V8Project([string]$startDir) { $d = $startDir for ($i = 0; $i -lt 20 -and $d; $i++) { @@ -448,6 +449,10 @@ Assert-InfoBaseExists $InfoBasePath # --- Resolve V8Path --- function Find-ProjectV8Path { + # v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + # платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + $dbRec = Find-ProjectDatabase + if ($dbRec -and $dbRec.v8path) { return [string]$dbRec.v8path } $dir = (Get-Location).Path while ($dir) { $pf = Join-Path $dir ".v8-project.json" diff --git a/.claude/skills/db-load-xml/scripts/db-load-xml.py b/.claude/skills/db-load-xml/scripts/db-load-xml.py index 23c81b9bd..71b1d7830 100644 --- a/.claude/skills/db-load-xml/scripts/db-load-xml.py +++ b/.claude/skills/db-load-xml/scripts/db-load-xml.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-load-xml v1.28 — Load 1C configuration from XML files +# db-load-xml v1.29 — Load 1C configuration from XML files # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -37,8 +37,15 @@ def ci_parse_args(parser, argv=None): -def _find_project_v8path(): - """Walk up from CWD to find .v8-project.json and read its v8path.""" +def _find_project_v8path(args): + """Walk up from CWD to find .v8-project.json and read its v8path. + + v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + """ + db = find_project_database(args) + if db and db.get("v8path"): + return db["v8path"] d = os.getcwd() while True: pf = os.path.join(d, ".v8-project.json") @@ -87,9 +94,10 @@ V8_SECRET_KEYS = ["/P", "/UC", "/WSP", "/AWSP", "/ConfigurationRepositoryP"] IBCMD_SECRET_KEYS = ["--password", "--token", "--db-pwd"] -# --- Реквизиты хранилища из .v8-project.json --- -# Модель их не передаёт: скрипт сопоставляет параметры соединения с записью в databases[] -# и берёт repository оттуда. Тот же приём, что в cf-edit.py (сопоставление по configSrc). +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.py (сопоставление по configSrc). def _sg_find_v8project(start_dir): d = start_dir for _ in range(20): @@ -349,10 +357,10 @@ def _version_key(p): return [int(x) for x in re.findall(r"\d+", _version_dir(p))] -def resolve_v8path(v8path): +def resolve_v8path(v8path, args): """Resolve path to a 1C executable (1cv8; ibcmd only when given explicitly).""" if not v8path: - v8path = _find_project_v8path() + v8path = _find_project_v8path(args) if not v8path: if os.name == "nt": candidates = ( @@ -692,7 +700,7 @@ def main(): args.ListFile = clean_path(args.ListFile, "-ListFile") # --- Resolve V8Path --- - v8path = resolve_v8path(args.V8Path) + v8path = resolve_v8path(args.V8Path, args) engine = "ibcmd" if os.path.basename(v8path).lower().startswith("ibcmd") else "1cv8" diff --git a/.claude/skills/db-repo/SKILL.md b/.claude/skills/db-repo/SKILL.md index 9e2eb0d8e..2e8f0c9e9 100644 --- a/.claude/skills/db-repo/SKILL.md +++ b/.claude/skills/db-repo/SKILL.md @@ -63,12 +63,14 @@ allowed-tools: ## Параметры подключения -Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` и разреши базу: +Прочитай `.v8-project.json` из корня проекта и разреши базу: 1. Если пользователь указал параметры подключения — используй напрямую 2. Если указал базу по имени — ищи по id / alias / name 3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` 4. Если ветка не совпала — используй `default` +Платформа — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. + Реквизиты хранилища передавать не нужно: запись базы находится по переданным параметрам соединения (`-InfoBasePath` либо `-InfoBaseServer` + `-InfoBaseRef`), реквизиты берутся из её `repository`. Задать их явно можно параметрами `-Repository*`. diff --git a/.claude/skills/db-repo/scripts/db-repo.ps1 b/.claude/skills/db-repo/scripts/db-repo.ps1 index 5c22ee75c..8c04efe95 100644 --- a/.claude/skills/db-repo/scripts/db-repo.ps1 +++ b/.claude/skills/db-repo/scripts/db-repo.ps1 @@ -1,4 +1,4 @@ -# db-repo v1.14 — 1C configuration repository operations +# db-repo v1.15 — 1C configuration repository operations # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: движок только 1cv8 — ibcmd работу с хранилищем не поддерживает (нет такого режима). <# @@ -322,6 +322,10 @@ Assert-InfoBaseExists $InfoBasePath # --- Resolve V8Path --- function Find-ProjectV8Path { + # v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + # платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + $dbRec = Find-ProjectDatabase + if ($dbRec -and $dbRec.v8path) { return [string]$dbRec.v8path } $dir = (Get-Location).Path while ($dir) { $pf = Join-Path $dir ".v8-project.json" @@ -499,9 +503,10 @@ function Invoke-PlatformProcess { return [pscustomobject]@{ Output = $out; ExitCode = $p.ExitCode } } -# --- Реквизиты хранилища из .v8-project.json --- -# Модель их не передаёт: скрипт сопоставляет параметры соединения с записью в databases[] -# и берёт repository оттуда. Тот же приём, что в cf-edit.ps1 (сопоставление по configSrc). +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.ps1 (сопоставление по configSrc). function Find-V8Project([string]$startDir) { $d = $startDir for ($i = 0; $i -lt 20 -and $d; $i++) { diff --git a/.claude/skills/db-repo/scripts/db-repo.py b/.claude/skills/db-repo/scripts/db-repo.py index 1e891a902..58bd50b8c 100644 --- a/.claude/skills/db-repo/scripts/db-repo.py +++ b/.claude/skills/db-repo/scripts/db-repo.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-repo v1.14 — 1C configuration repository operations +# db-repo v1.15 — 1C configuration repository operations # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: движок только 1cv8 — ibcmd работу с хранилищем не поддерживает (нет такого режима). """Работа с хранилищем конфигурации 1С. @@ -78,8 +78,15 @@ def ci_parse_args(parser, argv=None): return parser.parse_args(argv) -def _find_project_v8path(): - """Walk up from CWD to find .v8-project.json and read its v8path.""" +def _find_project_v8path(args): + """Walk up from CWD to find .v8-project.json and read its v8path. + + v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + """ + db = find_project_database(args) + if db and db.get("v8path"): + return db["v8path"] d = os.getcwd() while True: pf = os.path.join(d, ".v8-project.json") @@ -224,10 +231,10 @@ def _version_key(p): return [int(x) for x in re.findall(r"\d+", _version_dir(p))] -def resolve_v8path(v8path): +def resolve_v8path(v8path, args): """Resolve path to a 1C executable (1cv8; ibcmd only when given explicitly).""" if not v8path: - v8path = _find_project_v8path() + v8path = _find_project_v8path(args) if not v8path: if os.name == "nt": candidates = ( @@ -405,9 +412,10 @@ def resolve_extra_args(v8_extra, hints): return extra -# --- Реквизиты хранилища из .v8-project.json --- -# Модель их не передаёт: скрипт сопоставляет параметры соединения с записью в databases[] -# и берёт repository оттуда. Тот же приём, что в cf-edit.py (сопоставление по configSrc). +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.py (сопоставление по configSrc). def _sg_find_v8project(start_dir): d = start_dir for _ in range(20): @@ -983,7 +991,7 @@ def main(): args.V8Path = clean_path(args.V8Path, "-V8Path") args.InfoBasePath = clean_path(args.InfoBasePath, "-InfoBasePath") assert_infobase_exists(args.InfoBasePath) - v8path = resolve_v8path(args.V8Path) + v8path = resolve_v8path(args.V8Path, args) if not args.Command: print("Error: -Command is required. Known: %s" % ", ".join(sorted(COMMAND_KEYS))) diff --git a/.claude/skills/db-run/SKILL.md b/.claude/skills/db-run/SKILL.md index 132ef1087..2acba9294 100644 --- a/.claude/skills/db-run/SKILL.md +++ b/.claude/skills/db-run/SKILL.md @@ -24,12 +24,12 @@ allowed-tools: ## Параметры подключения -Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` (путь к платформе) и разреши базу: +Прочитай `.v8-project.json` из корня проекта и разреши базу: 1. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую 2. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` 3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` 4. Если ветка не совпала — используй `default` -Если `v8path` не задан — скрипт сам попытается определить платформу (`.v8-project.json` → Program Files). +Платформа — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. Если файла нет — предложи `/db-list add`. Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. diff --git a/.claude/skills/db-run/scripts/db-run.ps1 b/.claude/skills/db-run/scripts/db-run.ps1 index 340979d85..659675360 100644 --- a/.claude/skills/db-run/scripts/db-run.ps1 +++ b/.claude/skills/db-run/scripts/db-run.ps1 @@ -1,4 +1,4 @@ -# db-run v1.10 — Launch 1C:Enterprise +# db-run v1.11 — Launch 1C:Enterprise # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -262,8 +262,52 @@ $V8Path = ConvertTo-CleanPath $V8Path '-V8Path' $InfoBasePath = ConvertTo-CleanPath $InfoBasePath '-InfoBasePath' $Execute = ConvertTo-CleanPath $Execute '-Execute' +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.ps1 (сопоставление по configSrc). +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 Test-SamePath { + param([string]$A, [string]$B) + if (-not $A -or -not $B) { return $false } + try { + $na = [System.IO.Path]::GetFullPath($A).TrimEnd('\', '/') + $nb = [System.IO.Path]::GetFullPath($B).TrimEnd('\', '/') + return $na.Equals($nb, [System.StringComparison]::OrdinalIgnoreCase) + } catch { return $false } +} +function Find-ProjectDatabase { + # Запись базы в реестре, соответствующая переданному соединению. $null, если не найдена. + $pf = Find-V8Project (Get-Location).Path + if (-not $pf) { return $null } + try { $proj = Get-Content $pf -Raw -Encoding UTF8 | ConvertFrom-Json } catch { return $null } + if (-not $proj.databases) { return $null } + foreach ($db in $proj.databases) { + if ($InfoBasePath -and $db.path -and (Test-SamePath $db.path $InfoBasePath)) { return $db } + if ($InfoBaseServer -and $InfoBaseRef -and $db.server -and $db.ref) { + if ($db.server.Equals($InfoBaseServer, [System.StringComparison]::OrdinalIgnoreCase) -and + $db.ref.Equals($InfoBaseRef, [System.StringComparison]::OrdinalIgnoreCase)) { return $db } + } + } + return $null +} + # --- Resolve V8Path --- function Find-ProjectV8Path { + # v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + # платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + $dbRec = Find-ProjectDatabase + if ($dbRec -and $dbRec.v8path) { return [string]$dbRec.v8path } $dir = (Get-Location).Path while ($dir) { $pf = Join-Path $dir ".v8-project.json" diff --git a/.claude/skills/db-run/scripts/db-run.py b/.claude/skills/db-run/scripts/db-run.py index 24cc86d54..d1c9c794b 100644 --- a/.claude/skills/db-run/scripts/db-run.py +++ b/.claude/skills/db-run/scripts/db-run.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-run v1.10 — Launch 1C:Enterprise +# db-run v1.11 — Launch 1C:Enterprise # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -34,8 +34,62 @@ def ci_parse_args(parser, argv=None): -def _find_project_v8path(): - """Walk up from CWD to find .v8-project.json and read its v8path.""" +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.py (сопоставление по configSrc). +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 same_path(a, b): + if not a or not b: + return False + try: + return os.path.abspath(a).rstrip("\\/").lower() == os.path.abspath(b).rstrip("\\/").lower() + except Exception: + return False + + +def find_project_database(args): + """Запись базы в реестре, соответствующая переданному соединению. None, если не найдена.""" + pf = _sg_find_v8project(os.getcwd()) + if not pf: + return None + try: + with open(pf, encoding="utf-8-sig") as f: + proj = json.load(f) + except Exception: + return None + for db in proj.get("databases") or []: + if args.InfoBasePath and db.get("path") and same_path(db["path"], args.InfoBasePath): + return db + if args.InfoBaseServer and args.InfoBaseRef and db.get("server") and db.get("ref"): + if (db["server"].lower() == args.InfoBaseServer.lower() + and db["ref"].lower() == args.InfoBaseRef.lower()): + return db + return None + + +def _find_project_v8path(args): + """Walk up from CWD to find .v8-project.json and read its v8path. + + v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + """ + db = find_project_database(args) + if db and db.get("v8path"): + return db["v8path"] d = os.getcwd() while True: pf = os.path.join(d, ".v8-project.json") @@ -258,10 +312,10 @@ def _version_key(p): return [int(x) for x in re.findall(r"\d+", _version_dir(p))] -def resolve_v8path(v8path): +def resolve_v8path(v8path, args): """Resolve path to a 1C executable (1cv8; ibcmd only when given explicitly).""" if not v8path: - v8path = _find_project_v8path() + v8path = _find_project_v8path(args) if not v8path: if os.name == "nt": candidates = ( @@ -324,7 +378,7 @@ def main(): args.InfoBasePath = clean_path(args.InfoBasePath, "-InfoBasePath") args.Execute = clean_path(args.Execute, "-Execute") - v8path = resolve_v8path(args.V8Path) + v8path = resolve_v8path(args.V8Path, args) # --- Resolve additional arguments --- # 1C:Enterprise is always launched by 1cv8 — ibcmd has no interactive mode. diff --git a/.claude/skills/db-update/SKILL.md b/.claude/skills/db-update/SKILL.md index 3f7536c56..c02eef36a 100644 --- a/.claude/skills/db-update/SKILL.md +++ b/.claude/skills/db-update/SKILL.md @@ -25,12 +25,12 @@ allowed-tools: ## Параметры подключения -Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` (путь к платформе) и разреши базу: +Прочитай `.v8-project.json` из корня проекта и разреши базу: 1. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую 2. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` 3. Если не указал — сопоставь текущую ветку Git с `databases[].branches` 4. Если ветка не совпала — используй `default` -Если `v8path` не задан — скрипт сам попытается определить платформу (`.v8-project.json` → Program Files). +Платформа — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. Если файла нет — предложи `/db-list add`. Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. diff --git a/.claude/skills/db-update/scripts/db-update.ps1 b/.claude/skills/db-update/scripts/db-update.ps1 index 1321bcc74..c39f3ee8f 100644 --- a/.claude/skills/db-update/scripts/db-update.ps1 +++ b/.claude/skills/db-update/scripts/db-update.ps1 @@ -1,4 +1,4 @@ -# db-update v1.20 — Update 1C database configuration +# db-update v1.21 — Update 1C database configuration # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -124,9 +124,10 @@ if ($Dynamic) { $Dynamic = if (@('on', 'yes', '+') -contains $Dynamic.ToLower()) $OutputEncoding = [System.Text.Encoding]::UTF8 [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 -# --- Реквизиты хранилища из .v8-project.json --- -# Модель их не передаёт: скрипт сопоставляет параметры соединения с записью в databases[] -# и берёт repository оттуда. Тот же приём, что в cf-edit.ps1 (сопоставление по configSrc). +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.ps1 (сопоставление по configSrc). function Find-V8Project([string]$startDir) { $d = $startDir for ($i = 0; $i -lt 20 -and $d; $i++) { @@ -410,6 +411,10 @@ Assert-InfoBaseExists $InfoBasePath # --- Resolve V8Path --- function Find-ProjectV8Path { + # v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + # платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + $dbRec = Find-ProjectDatabase + if ($dbRec -and $dbRec.v8path) { return [string]$dbRec.v8path } $dir = (Get-Location).Path while ($dir) { $pf = Join-Path $dir ".v8-project.json" diff --git a/.claude/skills/db-update/scripts/db-update.py b/.claude/skills/db-update/scripts/db-update.py index ec3d0f5f1..1cb73b2ed 100644 --- a/.claude/skills/db-update/scripts/db-update.py +++ b/.claude/skills/db-update/scripts/db-update.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# db-update v1.20 — Update 1C database configuration +# db-update v1.21 — Update 1C database configuration # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -37,8 +37,15 @@ def ci_parse_args(parser, argv=None): -def _find_project_v8path(): - """Walk up from CWD to find .v8-project.json and read its v8path.""" +def _find_project_v8path(args): + """Walk up from CWD to find .v8-project.json and read its v8path. + + v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + """ + db = find_project_database(args) + if db and db.get("v8path"): + return db["v8path"] d = os.getcwd() while True: pf = os.path.join(d, ".v8-project.json") @@ -87,9 +94,10 @@ V8_SECRET_KEYS = ["/P", "/UC", "/WSP", "/AWSP", "/ConfigurationRepositoryP"] IBCMD_SECRET_KEYS = ["--password", "--token", "--db-pwd"] -# --- Реквизиты хранилища из .v8-project.json --- -# Модель их не передаёт: скрипт сопоставляет параметры соединения с записью в databases[] -# и берёт repository оттуда. Тот же приём, что в cf-edit.py (сопоставление по configSrc). +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.py (сопоставление по configSrc). def _sg_find_v8project(start_dir): d = start_dir for _ in range(20): @@ -328,10 +336,10 @@ def _version_key(p): return [int(x) for x in re.findall(r"\d+", _version_dir(p))] -def resolve_v8path(v8path): +def resolve_v8path(v8path, args): """Resolve path to a 1C executable (1cv8; ibcmd only when given explicitly).""" if not v8path: - v8path = _find_project_v8path() + v8path = _find_project_v8path(args) if not v8path: if os.name == "nt": candidates = ( @@ -659,7 +667,7 @@ def main(): args.InfoBasePath = clean_path(args.InfoBasePath, "-InfoBasePath") assert_infobase_exists(args.InfoBasePath) - v8path = resolve_v8path(args.V8Path) + v8path = resolve_v8path(args.V8Path, args) engine = "ibcmd" if os.path.basename(v8path).lower().startswith("ibcmd") else "1cv8" diff --git a/.claude/skills/epf-build/SKILL.md b/.claude/skills/epf-build/SKILL.md index 12ab01a0b..9f7d94644 100644 --- a/.claude/skills/epf-build/SKILL.md +++ b/.claude/skills/epf-build/SKILL.md @@ -28,14 +28,14 @@ allowed-tools: Предпочтительно использовать конкретную базу — это надёжнее. Временная база всё равно поднимается под проверку исходников, если она не отключена. -1. Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` и разреши базу: +1. Прочитай `.v8-project.json` из корня проекта и разреши базу: 2. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую 3. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` 4. Если не указал — сопоставь текущую ветку Git с `databases[].branches` 5. Если ветка не совпала — используй `default` 6. Если `.v8-project.json` нет или база не найдена — не указывай параметры подключения: скрипт автоматически создаст временную базу. Для EPF со ссылочными типами (CatalogRef, DocumentRef и т.д.) генерируются заглушки метаданных. Временная база удаляется после сборки. -Если `v8path` не задан — скрипт сам попытается определить платформу (`.v8-project.json` → Program Files). +Платформа — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. ## Команда diff --git a/.claude/skills/epf-build/scripts/epf-build.ps1 b/.claude/skills/epf-build/scripts/epf-build.ps1 index 5dd259092..d9a83db2d 100644 --- a/.claude/skills/epf-build/scripts/epf-build.ps1 +++ b/.claude/skills/epf-build/scripts/epf-build.ps1 @@ -1,4 +1,4 @@ -# epf-build v1.17 — Build external data processor or report (EPF/ERF) from XML sources +# epf-build v1.18 — Build external data processor or report (EPF/ERF) from XML sources # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -276,8 +276,41 @@ function Assert-InfoBaseExists { Assert-InfoBaseExists $InfoBasePath +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.ps1 (сопоставление по configSrc). +function Test-SamePath { + param([string]$A, [string]$B) + if (-not $A -or -not $B) { return $false } + try { + $na = [System.IO.Path]::GetFullPath($A).TrimEnd('\', '/') + $nb = [System.IO.Path]::GetFullPath($B).TrimEnd('\', '/') + return $na.Equals($nb, [System.StringComparison]::OrdinalIgnoreCase) + } catch { return $false } +} +function Find-ProjectDatabase { + # Запись базы в реестре, соответствующая переданному соединению. $null, если не найдена. + $pf = Find-V8Project (Get-Location).Path + if (-not $pf) { return $null } + try { $proj = Get-Content $pf -Raw -Encoding UTF8 | ConvertFrom-Json } catch { return $null } + if (-not $proj.databases) { return $null } + foreach ($db in $proj.databases) { + if ($InfoBasePath -and $db.path -and (Test-SamePath $db.path $InfoBasePath)) { return $db } + if ($InfoBaseServer -and $InfoBaseRef -and $db.server -and $db.ref) { + if ($db.server.Equals($InfoBaseServer, [System.StringComparison]::OrdinalIgnoreCase) -and + $db.ref.Equals($InfoBaseRef, [System.StringComparison]::OrdinalIgnoreCase)) { return $db } + } + } + return $null +} + # --- Resolve V8Path --- function Find-ProjectV8Path { + # v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + # платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + $dbRec = Find-ProjectDatabase + if ($dbRec -and $dbRec.v8path) { return [string]$dbRec.v8path } $dir = (Get-Location).Path while ($dir) { $pf = Join-Path $dir ".v8-project.json" diff --git a/.claude/skills/epf-build/scripts/epf-build.py b/.claude/skills/epf-build/scripts/epf-build.py index f7a41a560..3f363588f 100644 --- a/.claude/skills/epf-build/scripts/epf-build.py +++ b/.claude/skills/epf-build/scripts/epf-build.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# epf-build v1.17 — Build external data processor or report (EPF/ERF) from XML sources +# epf-build v1.18 — Build external data processor or report (EPF/ERF) from XML sources # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -38,8 +38,48 @@ def ci_parse_args(parser, argv=None): -def _find_project_v8path(): - """Walk up from CWD to find .v8-project.json and read its v8path.""" +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.py (сопоставление по configSrc). +def same_path(a, b): + if not a or not b: + return False + try: + return os.path.abspath(a).rstrip("\\/").lower() == os.path.abspath(b).rstrip("\\/").lower() + except Exception: + return False + + +def find_project_database(args): + """Запись базы в реестре, соответствующая переданному соединению. None, если не найдена.""" + pf = _sg_find_v8project(os.getcwd()) + if not pf: + return None + try: + with open(pf, encoding="utf-8-sig") as f: + proj = json.load(f) + except Exception: + return None + for db in proj.get("databases") or []: + if args.InfoBasePath and db.get("path") and same_path(db["path"], args.InfoBasePath): + return db + if args.InfoBaseServer and args.InfoBaseRef and db.get("server") and db.get("ref"): + if (db["server"].lower() == args.InfoBaseServer.lower() + and db["ref"].lower() == args.InfoBaseRef.lower()): + return db + return None + + +def _find_project_v8path(args): + """Walk up from CWD to find .v8-project.json and read its v8path. + + v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + """ + db = find_project_database(args) + if db and db.get("v8path"): + return db["v8path"] d = os.getcwd() while True: pf = os.path.join(d, ".v8-project.json") @@ -244,10 +284,10 @@ def _version_key(p): return [int(x) for x in re.findall(r"\d+", _version_dir(p))] -def resolve_v8path(v8path): +def resolve_v8path(v8path, args): """Resolve path to a 1C executable (1cv8; ibcmd only when given explicitly).""" if not v8path: - v8path = _find_project_v8path() + v8path = _find_project_v8path(args) if not v8path: if os.name == "nt": candidates = ( @@ -576,7 +616,7 @@ def main(): args.OutputFile = clean_path(args.OutputFile, "-OutputFile") # --- Resolve V8Path --- - v8path = resolve_v8path(args.V8Path) + v8path = resolve_v8path(args.V8Path, args) engine = "ibcmd" if os.path.basename(v8path).lower().startswith("ibcmd") else "1cv8" # --- Resolve additional arguments for the selected engine --- diff --git a/.claude/skills/epf-dump/SKILL.md b/.claude/skills/epf-dump/SKILL.md index 33fb7fdf4..e3943990b 100644 --- a/.claude/skills/epf-dump/SKILL.md +++ b/.claude/skills/epf-dump/SKILL.md @@ -26,14 +26,14 @@ allowed-tools: Для разборки EPF/ERF требуется информационная база с конфигурацией. Без базы ссылочные типы безвозвратно теряются. -1. Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` и разреши базу: +1. Прочитай `.v8-project.json` из корня проекта и разреши базу: 2. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую 3. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` 4. Если не указал — сопоставь текущую ветку Git с `databases[].branches` 5. Если ветка не совпала — используй `default` 6. Если `.v8-project.json` нет или база не найдена — **сообщи пользователю об ошибке**. Для dump база обязательна: в пустой базе ссылочные типы (CatalogRef, DocumentRef и т.д.) безвозвратно сбрасываются в строки. Предложи указать базу или зарегистрировать через `/db-list add`. -Если `v8path` не задан — скрипт сам попытается определить платформу (`.v8-project.json` → Program Files). +Платформа — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. ## Команда diff --git a/.claude/skills/epf-dump/scripts/epf-dump.ps1 b/.claude/skills/epf-dump/scripts/epf-dump.ps1 index 63ba0e577..ababe0ffe 100644 --- a/.claude/skills/epf-dump/scripts/epf-dump.ps1 +++ b/.claude/skills/epf-dump/scripts/epf-dump.ps1 @@ -1,4 +1,4 @@ -# epf-dump v1.15 — Dump external data processor or report (EPF/ERF) to XML sources +# epf-dump v1.16 — Dump external data processor or report (EPF/ERF) to XML sources # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills # NB: *nix-раскладку платформы (/opt/1cv8//1cv8, без .exe) знает только .py-порт — PS на *nix не исполняется. <# @@ -267,8 +267,52 @@ function Assert-InfoBaseExists { Assert-InfoBaseExists $InfoBasePath +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.ps1 (сопоставление по configSrc). +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 Test-SamePath { + param([string]$A, [string]$B) + if (-not $A -or -not $B) { return $false } + try { + $na = [System.IO.Path]::GetFullPath($A).TrimEnd('\', '/') + $nb = [System.IO.Path]::GetFullPath($B).TrimEnd('\', '/') + return $na.Equals($nb, [System.StringComparison]::OrdinalIgnoreCase) + } catch { return $false } +} +function Find-ProjectDatabase { + # Запись базы в реестре, соответствующая переданному соединению. $null, если не найдена. + $pf = Find-V8Project (Get-Location).Path + if (-not $pf) { return $null } + try { $proj = Get-Content $pf -Raw -Encoding UTF8 | ConvertFrom-Json } catch { return $null } + if (-not $proj.databases) { return $null } + foreach ($db in $proj.databases) { + if ($InfoBasePath -and $db.path -and (Test-SamePath $db.path $InfoBasePath)) { return $db } + if ($InfoBaseServer -and $InfoBaseRef -and $db.server -and $db.ref) { + if ($db.server.Equals($InfoBaseServer, [System.StringComparison]::OrdinalIgnoreCase) -and + $db.ref.Equals($InfoBaseRef, [System.StringComparison]::OrdinalIgnoreCase)) { return $db } + } + } + return $null +} + # --- Resolve V8Path --- function Find-ProjectV8Path { + # v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + # платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + $dbRec = Find-ProjectDatabase + if ($dbRec -and $dbRec.v8path) { return [string]$dbRec.v8path } $dir = (Get-Location).Path while ($dir) { $pf = Join-Path $dir ".v8-project.json" diff --git a/.claude/skills/epf-dump/scripts/epf-dump.py b/.claude/skills/epf-dump/scripts/epf-dump.py index b7a4d745b..45ecb00cc 100644 --- a/.claude/skills/epf-dump/scripts/epf-dump.py +++ b/.claude/skills/epf-dump/scripts/epf-dump.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# epf-dump v1.15 — Dump external data processor or report (EPF/ERF) to XML sources +# epf-dump v1.16 — Dump external data processor or report (EPF/ERF) to XML sources # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills import argparse @@ -37,8 +37,62 @@ def ci_parse_args(parser, argv=None): -def _find_project_v8path(): - """Walk up from CWD to find .v8-project.json and read its v8path.""" +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.py (сопоставление по configSrc). +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 same_path(a, b): + if not a or not b: + return False + try: + return os.path.abspath(a).rstrip("\\/").lower() == os.path.abspath(b).rstrip("\\/").lower() + except Exception: + return False + + +def find_project_database(args): + """Запись базы в реестре, соответствующая переданному соединению. None, если не найдена.""" + pf = _sg_find_v8project(os.getcwd()) + if not pf: + return None + try: + with open(pf, encoding="utf-8-sig") as f: + proj = json.load(f) + except Exception: + return None + for db in proj.get("databases") or []: + if args.InfoBasePath and db.get("path") and same_path(db["path"], args.InfoBasePath): + return db + if args.InfoBaseServer and args.InfoBaseRef and db.get("server") and db.get("ref"): + if (db["server"].lower() == args.InfoBaseServer.lower() + and db["ref"].lower() == args.InfoBaseRef.lower()): + return db + return None + + +def _find_project_v8path(args): + """Walk up from CWD to find .v8-project.json and read its v8path. + + v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + """ + db = find_project_database(args) + if db and db.get("v8path"): + return db["v8path"] d = os.getcwd() while True: pf = os.path.join(d, ".v8-project.json") @@ -243,10 +297,10 @@ def _version_key(p): return [int(x) for x in re.findall(r"\d+", _version_dir(p))] -def resolve_v8path(v8path): +def resolve_v8path(v8path, args): """Resolve path to a 1C executable (1cv8; ibcmd only when given explicitly).""" if not v8path: - v8path = _find_project_v8path() + v8path = _find_project_v8path(args) if not v8path: if os.name == "nt": candidates = ( @@ -446,7 +500,7 @@ def main(): args.OutputDir = clean_path(args.OutputDir, "-OutputDir") # --- Resolve V8Path --- - v8path = resolve_v8path(args.V8Path) + v8path = resolve_v8path(args.V8Path, args) engine = "ibcmd" if os.path.basename(v8path).lower().startswith("ibcmd") else "1cv8" # --- Resolve additional arguments for the selected engine --- diff --git a/.claude/skills/erf-build/SKILL.md b/.claude/skills/erf-build/SKILL.md index 2fbf0f859..066f90b40 100644 --- a/.claude/skills/erf-build/SKILL.md +++ b/.claude/skills/erf-build/SKILL.md @@ -28,14 +28,14 @@ allowed-tools: Предпочтительно использовать конкретную базу — это надёжнее. Временная база всё равно поднимается под проверку исходников, если она не отключена. -1. Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` и разреши базу: +1. Прочитай `.v8-project.json` из корня проекта и разреши базу: 2. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую 3. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` 4. Если не указал — сопоставь текущую ветку Git с `databases[].branches` 5. Если ветка не совпала — используй `default` 6. Если `.v8-project.json` нет или база не найдена — не указывай параметры подключения: скрипт автоматически создаст временную базу. Для ERF со ссылочными типами (CatalogRef, DocumentRef и т.д.) генерируются заглушки метаданных. Временная база удаляется после сборки. -Если `v8path` не задан — скрипт сам попытается определить платформу (`.v8-project.json` → Program Files). +Платформа — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. ## Команда diff --git a/.claude/skills/erf-dump/SKILL.md b/.claude/skills/erf-dump/SKILL.md index b1119922e..b94bedaca 100644 --- a/.claude/skills/erf-dump/SKILL.md +++ b/.claude/skills/erf-dump/SKILL.md @@ -26,14 +26,14 @@ allowed-tools: Для разборки EPF/ERF требуется информационная база с конфигурацией. Без базы ссылочные типы безвозвратно теряются. -1. Прочитай `.v8-project.json` из корня проекта. Возьми `v8path` и разреши базу: +1. Прочитай `.v8-project.json` из корня проекта и разреши базу: 2. Если пользователь указал параметры подключения (путь, сервер) — используй напрямую 3. Если указал базу по имени — ищи по id / alias / name в `.v8-project.json` 4. Если не указал — сопоставь текущую ветку Git с `databases[].branches` 5. Если ветка не совпала — используй `default` 6. Если `.v8-project.json` нет или база не найдена — **сообщи пользователю об ошибке**. Для dump база обязательна: в пустой базе ссылочные типы (CatalogRef, DocumentRef и т.д.) безвозвратно сбрасываются в строки. Предложи указать базу или зарегистрировать через `/db-list add`. -Если `v8path` не задан — скрипт сам попытается определить платформу (`.v8-project.json` → Program Files). +Платформа — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — `-V8Path` не передавай. Если использованная база не зарегистрирована — после выполнения предложи добавить через `/db-list add`. ## Команда diff --git a/.claude/skills/web-publish/SKILL.md b/.claude/skills/web-publish/SKILL.md index 25c973bd5..9605c9216 100644 --- a/.claude/skills/web-publish/SKILL.md +++ b/.claude/skills/web-publish/SKILL.md @@ -31,7 +31,7 @@ allowed-tools: 4. Если ветка не совпала — используй `default` **ОБЯЗАТЕЛЬНО передавай все найденные параметры:** -- **`-V8Path`** — из `v8path` в `.v8-project.json`. Если не передать, скрипт автоопределит версию платформы, что может выбрать не ту версию +- **`-V8Path`** — `v8path` найденной записи базы, иначе корневой `v8path`. Не задан ни там, ни там — не передавай, но версия платформы тогда выберется автоматически и может оказаться не той - **`-UserName`** — из поля `user` найденной записи базы (если есть) - **`-Password`** — из поля `password` найденной записи базы (если есть) - **`-ApachePath`** — из `webPath` в `.v8-project.json` (если есть) diff --git a/.claude/skills/web-publish/scripts/web-publish.ps1 b/.claude/skills/web-publish/scripts/web-publish.ps1 index 4bd5368d8..ff6f5ef83 100644 --- a/.claude/skills/web-publish/scripts/web-publish.ps1 +++ b/.claude/skills/web-publish/scripts/web-publish.ps1 @@ -1,4 +1,4 @@ -# web-publish v1.9 — Publish 1C infobase via Apache (+_version_dir/_version_key: общий эталон db-семейства) +# web-publish v1.10 — Publish 1C infobase via Apache (+_version_dir/_version_key: общий эталон db-семейства) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills <# .SYNOPSIS @@ -86,8 +86,52 @@ param( $OutputEncoding = [System.Text.Encoding]::UTF8 [Console]::OutputEncoding = [System.Text.Encoding]::UTF8 +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.ps1 (сопоставление по configSrc). +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 Test-SamePath { + param([string]$A, [string]$B) + if (-not $A -or -not $B) { return $false } + try { + $na = [System.IO.Path]::GetFullPath($A).TrimEnd('\', '/') + $nb = [System.IO.Path]::GetFullPath($B).TrimEnd('\', '/') + return $na.Equals($nb, [System.StringComparison]::OrdinalIgnoreCase) + } catch { return $false } +} +function Find-ProjectDatabase { + # Запись базы в реестре, соответствующая переданному соединению. $null, если не найдена. + $pf = Find-V8Project (Get-Location).Path + if (-not $pf) { return $null } + try { $proj = Get-Content $pf -Raw -Encoding UTF8 | ConvertFrom-Json } catch { return $null } + if (-not $proj.databases) { return $null } + foreach ($db in $proj.databases) { + if ($InfoBasePath -and $db.path -and (Test-SamePath $db.path $InfoBasePath)) { return $db } + if ($InfoBaseServer -and $InfoBaseRef -and $db.server -and $db.ref) { + if ($db.server.Equals($InfoBaseServer, [System.StringComparison]::OrdinalIgnoreCase) -and + $db.ref.Equals($InfoBaseRef, [System.StringComparison]::OrdinalIgnoreCase)) { return $db } + } + } + return $null +} + # --- Resolve V8Path --- function Find-ProjectV8Path { + # v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + # платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + $dbRec = Find-ProjectDatabase + if ($dbRec -and $dbRec.v8path) { return [string]$dbRec.v8path } $dir = (Get-Location).Path while ($dir) { $pf = Join-Path $dir ".v8-project.json" diff --git a/.claude/skills/web-publish/scripts/web-publish.py b/.claude/skills/web-publish/scripts/web-publish.py index 2a8df7db2..b53901865 100644 --- a/.claude/skills/web-publish/scripts/web-publish.py +++ b/.claude/skills/web-publish/scripts/web-publish.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# web-publish v1.9 — Publish 1C infobase via Apache (+_version_dir/_version_key: общий эталон db-семейства) +# web-publish v1.10 — Publish 1C infobase via Apache (+_version_dir/_version_key: общий эталон db-семейства) # Source: https://github.com/Nikolay-Shirokov/cc-1c-skills """ @@ -47,8 +47,62 @@ def ci_parse_args(parser, argv=None): -def _find_project_v8path(): - """Walk up from CWD to find .v8-project.json and read its v8path.""" +# --- Запись базы в .v8-project.json --- +# Модель не передаёт ни путь к платформе конкретной базы, ни реквизиты хранилища: скрипт +# сопоставляет параметры соединения с записью в databases[] и берёт их оттуда. Тот же приём, +# что в cf-edit.py (сопоставление по configSrc). +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 same_path(a, b): + if not a or not b: + return False + try: + return os.path.abspath(a).rstrip("\\/").lower() == os.path.abspath(b).rstrip("\\/").lower() + except Exception: + return False + + +def find_project_database(args): + """Запись базы в реестре, соответствующая переданному соединению. None, если не найдена.""" + pf = _sg_find_v8project(os.getcwd()) + if not pf: + return None + try: + with open(pf, encoding="utf-8-sig") as f: + proj = json.load(f) + except Exception: + return None + for db in proj.get("databases") or []: + if args.InfoBasePath and db.get("path") and same_path(db["path"], args.InfoBasePath): + return db + if args.InfoBaseServer and args.InfoBaseRef and db.get("server") and db.get("ref"): + if (db["server"].lower() == args.InfoBaseServer.lower() + and db["ref"].lower() == args.InfoBaseRef.lower()): + return db + return None + + +def _find_project_v8path(args): + """Walk up from CWD to find .v8-project.json and read its v8path. + + v8path записи базы сильнее корневого: в одном проекте базы живут на разных версиях + платформы, а версию формата выгрузки задаёт та платформа, которая выгружает. + """ + db = find_project_database(args) + if db and db.get("v8path"): + return db["v8path"] d = os.getcwd() while True: pf = os.path.join(d, ".v8-project.json") @@ -135,7 +189,7 @@ def main(): # --- Resolve V8Path --- v8_path = args.V8Path if not v8_path: - v8_path = _find_project_v8path() + v8_path = _find_project_v8path(args) if not v8_path: candidates = ( glob.glob(r'C:\Program Files\1cv8\*\bin\1cv8.exe') diff --git a/.v8-project.example.json b/.v8-project.example.json index 8b7302f82..d0a5e4ff3 100644 --- a/.v8-project.example.json +++ b/.v8-project.example.json @@ -6,6 +6,7 @@ "name": "Разработка", "type": "file", "path": "C:\\Bases\\MyApp_Dev", + "v8path": "C:\\Program Files\\1cv8\\8.3.27.1688\\bin", "user": "Администратор", "password": "", "aliases": [ diff --git a/docs/db-guide.md b/docs/db-guide.md index f79b48c4b..35f289442 100644 --- a/docs/db-guide.md +++ b/docs/db-guide.md @@ -139,7 +139,9 @@ Claude вызовет `/db-run dev`. > Собери обработку, платформа C:\Program Files\1cv8\8.3.24.1691\bin\ibcmd.exe ``` - **в файле настроек** — прописать в `v8path` не каталог `bin`, а сам файл `...\bin\ibcmd.exe` - (тогда через `ibcmd` пойдут все операции). + (тогда через `ibcmd` пойдут все операции). Это работает и в `v8path` отдельной базы — там же + задаётся её платформа, если базы проекта живут на разных версиях + (см. [конфигурацию проекта](v8-project-guide.md#платформа-базы-v8path)). Через `ibcmd` работают: `db-create`, `db-load-xml`/`db-dump-xml` (в том числе по отдельным объектам и со всеми расширениями), `db-load-cf`/`db-dump-cf`, `db-load-dt`/`db-dump-dt`, `db-update`, diff --git a/docs/v8-project-guide.md b/docs/v8-project-guide.md index 60d51c493..5e0821d66 100644 --- a/docs/v8-project-guide.md +++ b/docs/v8-project-guide.md @@ -22,6 +22,7 @@ "name": "Разработка", // отображаемое имя "type": "file", // "file" или "server" "path": "C:\\Bases\\MyApp_Dev", // каталог (для file) + "v8path": "C:\\Program Files\\1cv8\\8.3.27.1688\\bin", // платформа именно этой базы (см. ниже) "user": "Admin", // пользователь 1С "password": "", // пароль "aliases": ["dev", "разработка"], // альтернативные имена @@ -98,6 +99,7 @@ | `path` | string | для file | Каталог файловой базы | `/db-list add` | | `server` | string | для server | Адрес сервера 1С | `/db-list add` | | `ref` | string | для server | Имя базы на сервере | `/db-list add` | +| `v8path` | string | нет | Платформа этой базы — перекрывает корневой `v8path` (см. ниже) | Руками | | `user` | string | нет | Пользователь 1С | `/db-list add` или руками | | `password` | string | нет | Пароль | `/db-list add` или руками | | `aliases` | string[] | нет | Альтернативные имена для обращения к базе | `/db-list add` или руками | @@ -110,6 +112,33 @@ | `repository` | object | нет | Хранилище конфигурации: `path`, `user`, `password` (см. ниже) | Руками | | `extensions` | array | нет | Расширения конфигурации: `name`, `src`, необязательное `repository` (см. ниже) | Руками | +### Платформа базы (`v8path`) + +Корневой `v8path` — платформа проекта, `databases[].v8path` — платформа **этой** базы. Порядок +выбора у всех навыков, запускающих платформу: + +1. явный `-V8Path` в вызове; +2. `v8path` записи базы, соответствующей переданному соединению; +3. корневой `v8path`; +4. автопоиск — **самая старшая** установленная платформа. + +Запись ищет сам скрипт: сопоставляет параметры соединения с `databases[]` — файловую базу по +`path`, серверную по паре `server` + `ref`. Передавать `-V8Path` вручную не нужно. + +Поле закрывает два случая: + +- **базы проекта живут на разных версиях платформы** — каждая открывается своей; +- **формат XML-выгрузки** (`version` в `Configuration.xml`) задаёт та платформа, **которая + выгружает**; режим совместимости конфигурации на него не влияет. Выгрузка с 8.5 даёт формат 2.21, + и коллега на 8.3.27 её уже не загрузит: «Неизвестная версия формата». Прибив базе платформу, вы + прибиваете и формат её исходников. Без поля сработает автопоиск и возьмёт самую свежую + установленную платформу — то есть самый новый формат. + +Значение — тот же вид пути, что и у корневого: каталог `bin`, либо сам `1cv8.exe`/`ibcmd.exe` +(см. «Движок: 1cv8 или ibcmd»). Путь машинный, а `.v8-project.json` у каждого свой — договориться +внутри команды о версии всё равно придётся, поле лишь не даёт ошибиться при переключении между +базами. + ### Хранилище конфигурации (`repository`, `extensions[]`) База, подключённая к хранилищу конфигурации, не принимает **ни одной** операции конфигуратора без @@ -194,6 +223,8 @@ 5. Если не найдено — Claude спросит пользователя 6. Если база не зарегистрирована — Claude предложит `/db-list add` +Платформу для найденной базы подставляет сам скрипт — см. «Платформа базы (`v8path`)». + ## Настройки инструментов ### `webPath` — Apache HTTP Server @@ -263,7 +294,8 @@ src/ - **разово, в самой задаче** — назвать полный путь к `ibcmd.exe`, например `C:\\Program Files\\1cv8\\8.3.24.1691\\bin\\ibcmd.exe`; - **в файле настроек** — прописать в `v8path` не каталог `bin`, а сам файл `...\\bin\\ibcmd.exe` (тогда через - `ibcmd` пойдут все операции). + `ibcmd` пойдут все операции). То же работает и в `v8path` записи базы — тогда через `ibcmd` + пойдут операции только с ней. Через `ibcmd` навыки работают только с файловыми базами — для клиент-серверных `ibcmd` требует прямых реквизитов СУБД, которые навыки не запрашивают. Список навыков, работающих через `ibcmd`, — diff --git a/tests/skills/cases/db-create/v8path-from-database.json b/tests/skills/cases/db-create/v8path-from-database.json new file mode 100644 index 000000000..8a3721ad9 --- /dev/null +++ b/tests/skills/cases/db-create/v8path-from-database.json @@ -0,0 +1,24 @@ +{ + "name": "Платформа берётся из v8path записи базы, а не из корневого", + "fakePlatform": { "log": "" }, + "preRun": [ + { + "writeFile": { + "path": ".v8-project.json", + "content": { + "v8path": "{workDir}/nowhere/1cv8.exe", + "databases": [ + { "id": "dev", "name": "Dev", "type": "file", "path": "{workDir}/ib", "v8path": "{fakePlatform}" } + ] + } + } + } + ], + "args_extra": ["-InfoBasePath", "{workDir}/ib"], + "cwd": "workDir", + "expect": { + "stdoutContains": "Information base created successfully", + "stdoutNotContains": "nowhere" + }, + "noSnapshot": "проверяется выбор платформы по stdout, файлов на выходе нет" +} diff --git a/tests/skills/cases/db-create/v8path-no-record.json b/tests/skills/cases/db-create/v8path-no-record.json new file mode 100644 index 000000000..cb8374ca0 --- /dev/null +++ b/tests/skills/cases/db-create/v8path-no-record.json @@ -0,0 +1,22 @@ +{ + "name": "Соединение не совпало ни с одной записью — берётся корневой v8path", + "fakePlatform": { "log": "" }, + "preRun": [ + { + "writeFile": { + "path": ".v8-project.json", + "content": { + "v8path": "{workDir}/nowhere/1cv8.exe", + "databases": [ + { "id": "other", "name": "Other", "type": "file", "path": "{workDir}/other", "v8path": "{fakePlatform}" } + ] + } + } + } + ], + "args_extra": ["-InfoBasePath", "{workDir}/ib"], + "cwd": "workDir", + "expectError": true, + "expect": { "stdoutContains": "1C executable not found at" }, + "noSnapshot": "проверяется выбор платформы по stdout, файлов на выходе нет" +} diff --git a/tests/skills/cases/db-create/v8path-root-fallback.json b/tests/skills/cases/db-create/v8path-root-fallback.json new file mode 100644 index 000000000..6e3b5e5c3 --- /dev/null +++ b/tests/skills/cases/db-create/v8path-root-fallback.json @@ -0,0 +1,21 @@ +{ + "name": "У записи базы нет v8path — берётся корневой", + "fakePlatform": { "log": "" }, + "preRun": [ + { + "writeFile": { + "path": ".v8-project.json", + "content": { + "v8path": "{fakePlatform}", + "databases": [ + { "id": "dev", "name": "Dev", "type": "file", "path": "{workDir}/ib" } + ] + } + } + } + ], + "args_extra": ["-InfoBasePath", "{workDir}/ib"], + "cwd": "workDir", + "expect": { "stdoutContains": "Information base created successfully" }, + "noSnapshot": "проверяется выбор платформы по stdout, файлов на выходе нет" +} diff --git a/tests/skills/check-inline-drift.mjs b/tests/skills/check-inline-drift.mjs index eaadd3323..56510bd5a 100644 --- a/tests/skills/check-inline-drift.mjs +++ b/tests/skills/check-inline-drift.mjs @@ -88,10 +88,12 @@ const FAMILIES = [ variants: [ // Имя с префиксом _sg_ историческое: функция просто ищет .v8-project.json обходом // вверх. Группа db-* использует её же, чтобы найти запись базы и взять реквизиты - // хранилища — задача одна, поэтому семья общая, а не вторая с тем же телом. + // хранилища и путь к платформе — задача одна, поэтому семья общая, а не вторая с тем же телом. { id: 'full', authority: 'cf-edit', - consumers: ['cfe-borrow', 'db-cfe-admin', 'db-dump-xml', 'db-load-cf', 'db-load-git', 'db-load-xml', 'db-repo', 'db-update', - 'epf-build', 'form-add', 'form-compile', 'form-edit', 'help-add', 'interface-edit', 'meta-compile', + 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', + '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'] }, ], @@ -386,6 +388,16 @@ const FAMILIES = [ 'db-load-xml', 'db-repo', 'db-run', 'db-update', 'epf-build', 'epf-dump', 'web-publish', 'db-cfe-admin'] }, ], }, + { + // Обёртка выбора платформы: запись базы → корневой v8path → автопоиск. В PS1 её роль + // играет не функция, а блок кода после объявления Find-ProjectV8Path, поэтому ps1: null. + name: 'platform: resolve_v8path', py: 'resolve_v8path', ps1: null, + variants: [ + { id: 'base', authority: 'db-create', + consumers: ['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', 'db-cfe-admin'] }, + ], + }, { name: 'platform: find_project_v8path', py: '_find_project_v8path', ps1: 'Find-ProjectV8Path', variants: [ @@ -399,18 +411,24 @@ const FAMILIES = [ name: 'repository: load hints', py: 'write_repository_hints', ps1: 'Write-RepositoryHints', variants: [{ id: 'base', authority: 'db-load-xml', consumers: ['db-load-git'] }], }, - // ─── Реквизиты хранилища конфигурации ──────────────────────────────────── - // База под хранилищем не принимает ни одной операции конфигуратора без реквизитов - // доступа, и модель их не передаёт: скрипт сам сопоставляет параметры соединения с - // записью в databases[]. Блок копируется во все навыки группы, работающие с - // конфигурацией базы. + // ─── Запись базы в .v8-project.json ────────────────────────────────────── + // Модель не передаёт ни реквизиты хранилища (без них база под хранилищем не принимает + // ни одной операции конфигуратора), ни путь к платформе конкретной базы: скрипт сам + // сопоставляет параметры соединения с записью в databases[]. Матч один на обе задачи, + // поэтому блок копируется во все навыки, запускающие платформу. { name: 'repository: same_path', py: 'same_path', ps1: 'Test-SamePath', - variants: [{ id: 'base', authority: 'db-repo', consumers: ['db-dump-xml', 'db-load-git', 'db-load-xml', 'db-update', 'db-cfe-admin'] }], + variants: [{ id: 'base', authority: 'db-repo', + consumers: ['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-run', 'db-update', 'epf-build', 'epf-dump', + 'web-publish'] }], }, { name: 'repository: find_project_database', py: 'find_project_database', ps1: 'Find-ProjectDatabase', - variants: [{ id: 'base', authority: 'db-repo', consumers: ['db-dump-xml', 'db-load-git', 'db-load-xml', 'db-update', 'db-cfe-admin'] }], + variants: [{ id: 'base', authority: 'db-repo', + consumers: ['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-run', 'db-update', 'epf-build', 'epf-dump', + 'web-publish'] }], }, { name: 'repository: resolve_settings', py: 'resolve_repository_settings', ps1: 'Resolve-RepositorySettings', diff --git a/tests/skills/runner.mjs b/tests/skills/runner.mjs index 594837b18..7f4be4eab 100644 --- a/tests/skills/runner.mjs +++ b/tests/skills/runner.mjs @@ -274,6 +274,25 @@ function cleanupWorkspace(ws) { // ─── Arg building ─────────────────────────────────────────────────────────── +// Плейсхолдеры кейса: {workDir} — абсолютный путь рабочего каталога, {fakePlatform} — путь к +// фейковой платформе, которую раскладывает writeFakePlatform (имя зависит от ОС, поэтому кейс +// адресует её плейсхолдером и остаётся кроссплатформенным). Подстановка нужна не только в +// аргументах: путь к фейку кладут и ВНУТРЬ подложенного .v8-project.json — иначе кейс про +// выбор платформы пришлось бы писать двумя копиями под osOnly. +function substPlaceholders(s, workDir) { + const fakePath = join(workDir, process.platform === 'win32' ? 'fake.cmd' : 'fake.sh'); + return s.replaceAll('{workDir}', workDir).replaceAll('{fakePlatform}', fakePath); +} + +function substDeep(value, workDir) { + if (typeof value === 'string') return substPlaceholders(value, workDir); + if (Array.isArray(value)) return value.map(v => substDeep(v, workDir)); + if (value && typeof value === 'object') { + return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, substDeep(v, workDir)])); + } + return value; +} + // Байты входного файла в заданной кодировке. Нужно для кейсов про кодировку: writeFileSync // пишет только UTF-8, а навык обязан одинаково вести себя на UTF-16 с BOM (принять) и на // cp1251 (отвергнуть, а не молча подменить кириллицу на U+FFFD). cp1251 в Node нет — кодируем @@ -355,13 +374,8 @@ function buildArgs(skillConfig, caseData, workDir, inputFilePath, runtime) { } // Append extra args from case (for optional params like -Vendor, -Version). - // Supports {workDir} substitution for tests that need absolute paths inside the workspace, - // and {fakePlatform} — путь к фейковой платформе, которую раскладывает writeFakePlatform. if (caseData.args_extra) { - const fakePath = join(workDir, process.platform === 'win32' ? 'fake.cmd' : 'fake.sh'); - args.push(...caseData.args_extra.map(a => typeof a === 'string' - ? a.replace('{workDir}', workDir).replace('{fakePlatform}', fakePath) - : a)); + args.push(...caseData.args_extra.map(a => typeof a === 'string' ? substPlaceholders(a, workDir) : a)); } return { scriptPath, args }; @@ -837,9 +851,11 @@ async function runCaseAsync(testCase, opts) { // writeFile step — записать произвольный файл в workDir перед запуском скрипта if (step.writeFile) { const wfPath = join(workDir, step.writeFile.path); + // Плейсхолдеры подставляются и здесь. В объектной форме — ДО JSON.stringify: иначе + // обратные слэши windows-пути попали бы в готовый JSON неэкранированными. const wfContent = typeof step.writeFile.content === 'string' - ? step.writeFile.content - : JSON.stringify(step.writeFile.content, null, 2); + ? substPlaceholders(step.writeFile.content, workDir) + : JSON.stringify(substDeep(step.writeFile.content, workDir), null, 2); mkdirSync(dirname(wfPath), { recursive: true }); writeFileSync(wfPath, wfContent, 'utf8'); // Бит исполнения: на *nix навык запускает платформу через exec, и фейк без +x